comparison python/other/diagramitems.py @ 390:b77f3290ac79

Added diagram editor tests
author Windel Bouwman
date Fri, 16 May 2014 10:12:16 +0200
parents 534b94b40aa8
children bb4289c84907
comparison
equal deleted inserted replaced
388:e07c2a9abac1 390:b77f3290ac79
1 """ 1 """
2 Contains all blocks that can be used to build models. 2 Contains all blocks that can be used to build models.
3 """ 3 """
4 4
5 from PyQt4.QtGui import * 5 import sys
6 from PyQt4.QtCore import * 6 import json
7 import base64
8 import os
9
10 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'ide'))
11
12 from qtwrapper import QtGui, QtCore, QtWidgets, pyqtSignal, get_icon
13 from qtwrapper import abspath, Qt
14
7 15
8 def uniqify(name, names): 16 def uniqify(name, names):
9 newname, i = name, 1 17 newname, i = name, 1
10 while newname in names: newname, i = name + str(i), i + 1 18 while newname in names: newname, i = name + str(i), i + 1
11 return newname 19 return newname
20
12 21
13 def enum(**enums): 22 def enum(**enums):
14 return type('Enum', (), enums) 23 return type('Enum', (), enums)
24
15 25
16 Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7) 26 Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7)
17 27
28
18 def buildPath(pts): 29 def buildPath(pts):
19 path = QPainterPath(pts[0]) 30 path = QtGui.QPainterPath(pts[0])
20 for pt in pts[1:]: path.lineTo(pt) 31 for pt in pts[1:]: path.lineTo(pt)
21 return path 32 return path
22 33
23 def equalSpace(n, l, offset=15): 34 def equalSpace(n, l, offset=15):
24 if n == 1: 35 if n == 1:
25 return [l / 2] 36 return [l / 2]
26 elif n > 1: 37 elif n > 1:
27 return [offset + (l - offset*2)/(n - 1)*i for i in range(n)] 38 return [offset + (l - offset*2)/(n - 1)*i for i in range(n)]
28 return [] 39 return []
29 40
30 class Connection(QGraphicsPathItem): 41
42 class Connection(QtWidgets.QGraphicsPathItem):
31 """ A connection between blocks """ 43 """ A connection between blocks """
32 def __init__(self, fromPort=None, toPort=None): 44 def __init__(self, fromPort=None, toPort=None):
33 super(Connection, self).__init__() 45 super(Connection, self).__init__()
34 self.pos2 = self.fromPort = self.toPort = None 46 self.pos2 = self.fromPort = self.toPort = None
35 self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape) 47 self.setFlags(self.ItemIsSelectable | self.ItemClipsToShape)
36 pen = QPen(Qt.blue, 2, cap=Qt.RoundCap) 48 pen = QtGui.QPen(Qt.blue, 2, cap=Qt.RoundCap)
37 self.setPen(pen) 49 self.setPen(pen)
38 self.arrowhead = QGraphicsPathItem(self) 50 self.arrowhead = QtGui.QGraphicsPathItem(self)
39 self.arrowhead.setPath(buildPath([QPointF(0.0, 0.0), QPointF(-6.0, 10.0), QPointF(6.0, 10.0), QPointF(0.0, 0.0)])) 51 self.arrowhead.setPath(buildPath([QPointF(0.0, 0.0), QPointF(-6.0, 10.0), QPointF(6.0, 10.0), QPointF(0.0, 0.0)]))
40 self.arrowhead.setPen(pen) 52 self.arrowhead.setPen(pen)
41 self.arrowhead.setBrush(QBrush(pen.color())) 53 self.arrowhead.setBrush(QtGui.QBrush(pen.color()))
42 self.vias = [] 54 self.vias = []
43 self.setFromPort(fromPort) 55 self.setFromPort(fromPort)
44 self.setToPort(toPort) 56 self.setToPort(toPort)
45 def getDict(self): 57 def getDict(self):
46 d = {} 58 d = {}
97 if pos1 is None or pos2 is None: 109 if pos1 is None or pos2 is None:
98 return 110 return
99 scene = self.scene() 111 scene = self.scene()
100 vias = [pos1 + QPointF(20, 0)] + self.vias + [pos2 + QPointF(-20, 0)] 112 vias = [pos1 + QPointF(20, 0)] + self.vias + [pos2 + QPointF(-20, 0)]
101 if scene: 113 if scene:
102 litem = QGraphicsLineItem() 114 litem = QtGui.QGraphicsLineItem()
103 litem.setFlags(self.ItemIsSelectable) 115 litem.setFlags(self.ItemIsSelectable)
104 scene.addItem(litem) 116 scene.addItem(litem)
105 for p1, p2 in zip(vias[:-1], vias[1:]): 117 for p1, p2 in zip(vias[:-1], vias[1:]):
106 line = QLineF(p1, p2) 118 line = QLineF(p1, p2)
107 litem.setLine(line) 119 litem.setLine(line)
113 self.arrowhead.setRotation(90) 125 self.arrowhead.setRotation(90)
114 p = buildPath(pts) 126 p = buildPath(pts)
115 self.setPath(p) 127 self.setPath(p)
116 """ Create a shape outline using the path stroker """ 128 """ Create a shape outline using the path stroker """
117 s = super(Connection, self).shape() 129 s = super(Connection, self).shape()
118 pps = QPainterPathStroker() 130 pps = QtGui.QPainterPathStroker()
119 pps.setWidth(10) 131 pps.setWidth(10)
120 self.myshape = pps.createStroke(s).simplified() 132 self.myshape = pps.createStroke(s).simplified()
121 133
122 class PortItem(QGraphicsPathItem): 134 class PortItem(QtWidgets.QGraphicsPathItem):
123 """ Represents a port to a subsystem """ 135 """ Represents a port to a subsystem """
124 def __init__(self, name, block): 136 def __init__(self, name, block):
125 super(PortItem, self).__init__(block) 137 super(PortItem, self).__init__(block)
126 self.textItem = QGraphicsTextItem(self) 138 self.textItem = QtGui.QGraphicsTextItem(self)
127 self.connection = None 139 self.connection = None
128 self.block = block 140 self.block = block
129 self.setCursor(QCursor(Qt.CrossCursor)) 141 self.setCursor(QtGui.QCursor(Qt.CrossCursor))
130 self.setPen(QPen(Qt.blue, 2, cap=Qt.RoundCap)) 142 self.setPen(QtGui.QPen(Qt.blue, 2, cap=Qt.RoundCap))
131 self.name = name 143 self.name = name
132 self.posCallbacks = [] 144 self.posCallbacks = []
133 self.setFlag(self.ItemSendsScenePositionChanges, True) 145 self.setFlag(self.ItemSendsScenePositionChanges, True)
134 def getName(self): return self.textItem.toPlainText() 146 def getName(self): return self.textItem.toPlainText()
135 def setName(self, name): 147 def setName(self, name):
158 class InputPort(PortItem): 170 class InputPort(PortItem):
159 def __init__(self, name, block, d=10.0): 171 def __init__(self, name, block, d=10.0):
160 super(InputPort, self).__init__(name, block) 172 super(InputPort, self).__init__(name, block)
161 self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)])) 173 self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)]))
162 174
163 class Handle(QGraphicsEllipseItem): 175 class Handle(QtWidgets.QGraphicsEllipseItem):
164 """ A handle that can be moved by the mouse """ 176 """ A handle that can be moved by the mouse """
165 def __init__(self, dx=10.0, parent=None): 177 def __init__(self, dx=10.0, parent=None):
166 super(Handle, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) 178 super(Handle, self).__init__(QtCore.QRectF(-0.5*dx,-0.5*dx,dx,dx), parent)
167 self.setBrush(QBrush(Qt.white)) 179 self.setBrush(QtGui.QBrush(Qt.white))
168 self.setFlags(self.ItemIsMovable) 180 self.setFlags(self.ItemIsMovable)
169 self.setZValue(1) 181 self.setZValue(1)
170 self.setVisible(False) 182 self.setVisible(False)
171 self.setCursor(QCursor(Qt.SizeFDiagCursor)) 183 self.setCursor(QtGui.QCursor(Qt.SizeFDiagCursor))
172 def mouseMoveEvent(self, event): 184 def mouseMoveEvent(self, event):
173 """ Move function without moving the other selected elements """ 185 """ Move function without moving the other selected elements """
174 p = self.mapToParent(event.pos()) 186 p = self.mapToParent(event.pos())
175 self.setPos(p) 187 self.setPos(p)
176 188
178 def __init__(self, position, block): 190 def __init__(self, position, block):
179 super(ResizeSelectionHandle, self).__init__(dx=12, parent=block) 191 super(ResizeSelectionHandle, self).__init__(dx=12, parent=block)
180 self.position = position 192 self.position = position
181 self.block = block 193 self.block = block
182 if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: 194 if position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]:
183 self.setCursor(QCursor(Qt.SizeFDiagCursor)) 195 self.setCursor(QtGui.QCursor(Qt.SizeFDiagCursor))
184 elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: 196 elif position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]:
185 self.setCursor(QCursor(Qt.SizeBDiagCursor)) 197 self.setCursor(QCursor(Qt.SizeBDiagCursor))
186 elif position in [Position.TOP, Position.BOTTOM]: 198 elif position in [Position.TOP, Position.BOTTOM]:
187 self.setCursor(QCursor(Qt.SizeVerCursor)) 199 self.setCursor(QCursor(Qt.SizeVerCursor))
188 elif position in [Position.LEFT, Position.RIGHT]: 200 elif position in [Position.LEFT, Position.RIGHT]:
189 self.setCursor(QCursor(Qt.SizeHorCursor)) 201 self.setCursor(QtGui.QCursor(Qt.SizeHorCursor))
190 def mouseMoveEvent(self, event): 202 def mouseMoveEvent(self, event):
191 self.block.sizerMoveEvent(self, event.scenePos()) 203 self.block.sizerMoveEvent(self, event.scenePos())
192 204
193 class Block(QGraphicsRectItem): 205 class Block(QtWidgets.QGraphicsRectItem):
194 """ Represents a block in the diagram. """ 206 """ Represents a block in the diagram. """
195 def __init__(self, name='Untitled', parent=None): 207 def __init__(self, name='Untitled', parent=None):
196 super(Block, self).__init__(parent) 208 super(Block, self).__init__(parent)
197 self.selectionHandles = [ResizeSelectionHandle(i, self) for i in range(8)] 209 self.selectionHandles = [ResizeSelectionHandle(i, self) for i in range(8)]
198 # Properties of the rectangle: 210 # Properties of the rectangle:
199 self.setPen(QPen(Qt.blue, 2)) 211 self.setPen(QtGui.QPen(Qt.blue, 2))
200 self.setBrush(QBrush(Qt.lightGray)) 212 self.setBrush(QtGui.QBrush(Qt.lightGray))
201 self.setFlags(self.ItemIsSelectable | self.ItemIsMovable | self.ItemSendsScenePositionChanges) 213 self.setFlags(self.ItemIsSelectable | self.ItemIsMovable | self.ItemSendsScenePositionChanges)
202 self.setCursor(QCursor(Qt.PointingHandCursor)) 214 self.setCursor(QtGui.QCursor(Qt.PointingHandCursor))
203 self.setAcceptHoverEvents(True) 215 self.setAcceptHoverEvents(True)
204 self.label = QGraphicsTextItem(name, self) 216 self.label = QtWidgets.QGraphicsTextItem(name, self)
205 self.name = name 217 self.name = name
206 # Create corner for resize: 218 # Create corner for resize:
207 button = QPushButton('+in') 219 button = QtWidgets.QPushButton('+in')
208 button.clicked.connect(self.newInputPort) 220 button.clicked.connect(self.newInputPort)
209 self.buttonItemAddInput = QGraphicsProxyWidget(self) 221 self.buttonItemAddInput = QtWidgets.QGraphicsProxyWidget(self)
210 self.buttonItemAddInput.setWidget(button) 222 self.buttonItemAddInput.setWidget(button)
211 self.buttonItemAddInput.setVisible(False) 223 self.buttonItemAddInput.setVisible(False)
212 button = QPushButton('+out') 224 button = QtWidgets.QPushButton('+out')
213 button.clicked.connect(self.newOutputPort) 225 button.clicked.connect(self.newOutputPort)
214 self.buttonItemAddOutput = QGraphicsProxyWidget(self) 226 self.buttonItemAddOutput = QtWidgets.QGraphicsProxyWidget(self)
215 self.buttonItemAddOutput.setWidget(button) 227 self.buttonItemAddOutput.setWidget(button)
216 self.buttonItemAddOutput.setVisible(False) 228 self.buttonItemAddOutput.setVisible(False)
217 # Inputs and outputs of the block: 229 # Inputs and outputs of the block:
218 self.inputs = [] 230 self.inputs = []
219 self.outputs = [] 231 self.outputs = []
327 self.setRect(0.0, 0.0, w, h) 339 self.setRect(0.0, 0.0, w, h)
328 rect = self.label.boundingRect() 340 rect = self.label.boundingRect()
329 self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) 341 self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2)
330 self.updateSize() 342 self.updateSize()
331 343
344
332 class CodeBlock(Block): 345 class CodeBlock(Block):
333 def __init__(self, name='Untitled', parent=None): 346 def __init__(self, name='Untitled', parent=None):
334 super(CodeBlock, self).__init__(name, parent) 347 super(CodeBlock, self).__init__(name, parent)
335 self.code = '' 348 self.code = ''
336 def setDict(self, d): 349
337 super(CodeBlock, self).setDict(d) 350 def setDict(self, d):
338 self.code = d['code'] 351 super(CodeBlock, self).setDict(d)
339 def getDict(self): 352 self.code = d['code']
340 d = super(CodeBlock, self).getDict() 353
341 d['code'] = self.code 354 def getDict(self):
342 return d 355 d = super(CodeBlock, self).getDict()
343 def gencode(self): 356 d['code'] = self.code
357 return d
358
359 def gencode(self):
344 c = ['def {0}():'.format(self.name)] 360 c = ['def {0}():'.format(self.name)]
345 if self.code: 361 if self.code:
346 c += indent(self.code.split('\n')) 362 c += indent(self.code.split('\n'))
347 else: 363 else:
348 c += indent(['pass']) 364 c += indent(['pass'])
349 return c 365 return c
350 366
367
351 class DiagramBlock(Block): 368 class DiagramBlock(Block):
352 def __init__(self, name='Untitled', parent=None): 369 def __init__(self, name='Untitled', parent=None):
353 super(DiagramBlock, self).__init__(name, parent) 370 super(DiagramBlock, self).__init__(name, parent)
354 self.subModel = DiagramScene() 371 self.subModel = DiagramScene()
355 self.subModel.containingBlock = self 372 self.subModel.containingBlock = self
356 def setDict(self, d): 373
357 self.subModel.Dict = d['submodel'] 374 def setDict(self, d):
358 def mouseDoubleClickEvent(self, event): 375 self.subModel.Dict = d['submodel']
376
377 def mouseDoubleClickEvent(self, event):
359 # descent into child diagram 378 # descent into child diagram
360 #self.editParameters() 379 #self.editParameters()
361 print('descent') 380 print('descent')
362 scene = self.scene() 381 scene = self.scene()
363 if scene: 382 if scene:
364 for view in scene.views(): 383 for view in scene.views():
365 view.diagram = self.subModel 384 view.diagram = self.subModel
366 view.zoomAll() 385 view.zoomAll()
367 386
368 class DiagramScene(QGraphicsScene): 387
388 class DiagramScene(QtWidgets.QGraphicsScene):
369 """ A diagram scene consisting of blocks and connections """ 389 """ A diagram scene consisting of blocks and connections """
370 structureChanged = pyqtSignal() 390 structureChanged = pyqtSignal()
371 def __init__(self): 391 def __init__(self):
372 super(DiagramScene, self).__init__() 392 super(DiagramScene, self).__init__()
373 self.startedConnection = None 393 self.startedConnection = None