comparison python/apps/diagrameditor.py @ 74:f506f6b74697

Cleanup
author windel
date Sun, 11 Nov 2012 10:49:46 +0100
parents 191d70a0ed52
children ba11d99b1d4a
comparison
equal deleted inserted replaced
73:191d70a0ed52 74:f506f6b74697
17 path = QPainterPath(pts[0]) 17 path = QPainterPath(pts[0])
18 for pt in pts[1:]: 18 for pt in pts[1:]:
19 path.lineTo(pt) 19 path.lineTo(pt)
20 return path 20 return path
21 21
22 def equalSpace(n, l):
23 offset = 15
24 if n == 1:
25 return [l / 2]
26 elif n > 1:
27 return [offset + (l - offset*2)/(n - 1)*i for i in range(n)]
28 return []
29
22 class Connection(QGraphicsPathItem): 30 class Connection(QGraphicsPathItem):
23 """ Implementation of a connection between blocks """ 31 """ Implementation of a connection between blocks """
24 def __init__(self, fromPort, toPort): 32 def __init__(self, fromPort, toPort):
25 super(Connection, self).__init__() 33 super(Connection, self).__init__()
26 self.pos1, self.pos2 = None, None 34 self.pos2 = self.fromPort = self.toPort = None
27 self.fromPort, self.toPort = None, None
28 self.setFlag(self.ItemIsSelectable, True) 35 self.setFlag(self.ItemIsSelectable, True)
29 self.setFlag(self.ItemClipsToShape, True) 36 self.setFlag(self.ItemClipsToShape, True)
30 pen = QPen(Qt.blue, 2) 37 pen = QPen(Qt.blue, 2)
31 pen.setCapStyle(Qt.RoundCap) 38 pen.setCapStyle(Qt.RoundCap)
32 self.setPen(pen) 39 self.setPen(pen)
37 self.vias = [] 44 self.vias = []
38 self.setFromPort(fromPort) 45 self.setFromPort(fromPort)
39 self.setToPort(toPort) 46 self.setToPort(toPort)
40 def mouseDoubleClickEvent(self, event): 47 def mouseDoubleClickEvent(self, event):
41 pos = event.scenePos() 48 pos = event.scenePos()
42 pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2] 49 pts = [self.getPos1()] + [v.pos() for v in self.vias] + [self.pos2]
43 idx = 0 50 idx = 0
44 tidx = 0 51 tidx = 0
45 for p1, p2 in zip(pts[0:-1], pts[1:]): 52 for p1, p2 in zip(pts[0:-1], pts[1:]):
46 l1 = QLineF(p1, p2) 53 l1 = QLineF(p1, p2)
47 l2 = QLineF(p1, pos) 54 l2 = QLineF(p1, pos)
68 scene = self.scene() 75 scene = self.scene()
69 if scene: 76 if scene:
70 self.setFromPort(None) 77 self.setFromPort(None)
71 self.setToPort(None) 78 self.setToPort(None)
72 scene.removeItem(self) 79 scene.removeItem(self)
73
74 def setFromPort(self, fromPort): 80 def setFromPort(self, fromPort):
75 if self.fromPort: 81 if self.fromPort:
76 self.fromPort.posCallbacks.remove(self.setBeginPos) 82 self.fromPort.posCallbacks.remove(self.setBeginPos)
77 self.fromPort.connection = None 83 self.fromPort.connection = None
78 self.fromPort = fromPort 84 self.fromPort = fromPort
79 if self.fromPort: 85 if self.fromPort:
80 self.fromPort.connection = self 86 self.fromPort.connection = self
81 self.setBeginPos(fromPort.scenePos()) 87 self.updateLineStukken()
82 self.fromPort.posCallbacks.append(self.setBeginPos) 88 self.fromPort.posCallbacks.append(self.setBeginPos)
83 def setToPort(self, toPort): 89 def setToPort(self, toPort):
84 if self.toPort: 90 if self.toPort:
85 self.toPort.posCallbacks.remove(self.setEndPos) 91 self.toPort.posCallbacks.remove(self.setEndPos)
86 self.toPort.connection = None 92 self.toPort.connection = None
87 self.toPort = toPort 93 self.toPort = toPort
88 if self.toPort: 94 if self.toPort:
89 self.setEndPos(toPort.scenePos()) 95 self.setEndPos(toPort.scenePos())
90 self.toPort.connection = self 96 self.toPort.connection = self
91 self.toPort.posCallbacks.append(self.setEndPos) 97 self.toPort.posCallbacks.append(self.setEndPos)
98 def getPos1(self):
99 if self.fromPort:
100 return self.fromPort.scenePos()
92 def setBeginPos(self, pos1): 101 def setBeginPos(self, pos1):
93 self.pos1 = pos1
94 self.updateLineStukken() 102 self.updateLineStukken()
95 def setEndPos(self, endpos): 103 def setEndPos(self, endpos):
96 self.pos2 = endpos 104 self.pos2 = endpos
97 self.updateLineStukken() 105 self.updateLineStukken()
98 def itemChange(self, change, value): 106 def itemChange(self, change, value):
105 def updateLineStukken(self): 113 def updateLineStukken(self):
106 """ 114 """
107 This algorithm determines the optimal routing of all signals. 115 This algorithm determines the optimal routing of all signals.
108 TODO: implement nice automatic line router 116 TODO: implement nice automatic line router
109 """ 117 """
110 if self.pos1 is None or self.pos2 is None: 118 pos1 = self.getPos1()
119 if pos1 is None or self.pos2 is None:
111 return 120 return
112 pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2] 121 pts = [pos1] + [v.pos() for v in self.vias] + [self.pos2]
113 self.arrowhead.setPos(self.pos2) 122 self.arrowhead.setPos(self.pos2)
114 if pts[-1].x() < pts[-2].x(): 123 if pts[-1].x() < pts[-2].x():
115 self.arrowhead.setRotation(-90) 124 self.arrowhead.setRotation(-90)
116 else: 125 else:
117 self.arrowhead.setRotation(90) 126 self.arrowhead.setRotation(90)
162 self.block = block 171 self.block = block
163 self.setCursor(QCursor(Qt.CrossCursor)) 172 self.setCursor(QCursor(Qt.CrossCursor))
164 pen = QPen(Qt.blue, 2) 173 pen = QPen(Qt.blue, 2)
165 pen.setCapStyle(Qt.RoundCap) 174 pen.setCapStyle(Qt.RoundCap)
166 self.setPen(pen) 175 self.setPen(pen)
167 self.name = name
168 self.textItem = QGraphicsTextItem(name, self) 176 self.textItem = QGraphicsTextItem(name, self)
169 self.setName(name) 177 self.setName(name)
170 self.posCallbacks = [] 178 self.posCallbacks = []
171 self.setFlag(self.ItemSendsScenePositionChanges, True) 179 self.setFlag(self.ItemSendsScenePositionChanges, True)
172 def setName(self, name): 180 def setName(self, name):
206 self.setVisible(False) 214 self.setVisible(False)
207 self.setCursor(QCursor(Qt.SizeFDiagCursor)) 215 self.setCursor(QCursor(Qt.SizeFDiagCursor))
208 def mouseMoveEvent(self, event): 216 def mouseMoveEvent(self, event):
209 """ Move function without moving the other selected elements """ 217 """ Move function without moving the other selected elements """
210 p = self.mapToParent(event.pos()) 218 p = self.mapToParent(event.pos())
211 self.setPos(p)
212 def mySetPos(self, p):
213 # TODO: use this instead of itemChange?
214 self.setPos(p) 219 self.setPos(p)
215 def itemChange(self, change, value): 220 def itemChange(self, change, value):
216 if change == self.ItemPositionChange: 221 if change == self.ItemPositionChange:
217 for cb in self.posChangeCallbacks: 222 for cb in self.posChangeCallbacks:
218 res = cb(value) 223 res = cb(value)
262 button = QPushButton('+out') 267 button = QPushButton('+out')
263 button.clicked.connect(self.newOutputPort) 268 button.clicked.connect(self.newOutputPort)
264 self.buttonItemAddOutput = QGraphicsProxyWidget(self) 269 self.buttonItemAddOutput = QGraphicsProxyWidget(self)
265 self.buttonItemAddOutput.setWidget(button) 270 self.buttonItemAddOutput.setWidget(button)
266 self.buttonItemAddOutput.setVisible(False) 271 self.buttonItemAddOutput.setVisible(False)
267
268 # Inputs and outputs of the block: 272 # Inputs and outputs of the block:
269 self.inputs = [] 273 self.inputs = []
270 self.outputs = [] 274 self.outputs = []
271 # Update size: 275 # Update size:
272 self.sizer.mySetPos(QPointF(60, 40)) # This is a better resize function 276 self.sizer.setPos(QPointF(60, 40)) # This is a better resize function
273 def editParameters(self): 277 def editParameters(self):
274 pd = ParameterDialog(self, self.window()) 278 pd = ParameterDialog(self, self.window())
275 pd.exec_() 279 pd.exec_()
276 def mouseDoubleClickEvent(self, event): 280 def mouseDoubleClickEvent(self, event):
277 self.editParameters() 281 self.editParameters()
288 self.inputs.append(i) 292 self.inputs.append(i)
289 self.updateSize() 293 self.updateSize()
290 def addOutput(self, o): 294 def addOutput(self, o):
291 self.outputs.append(o) 295 self.outputs.append(o)
292 self.updateSize() 296 self.updateSize()
293
294 def contextMenuEvent(self, event): 297 def contextMenuEvent(self, event):
295 menu = QMenu() 298 menu = QMenu()
296 pa = menu.addAction('Parameters') 299 pa = menu.addAction('Parameters')
297 pa.triggered.connect(self.editParameters) 300 pa.triggered.connect(self.editParameters)
298 menu.exec_(event.screenPos()) 301 menu.exec_(event.screenPos())
300 if change == self.ItemSelectedHasChanged: 303 if change == self.ItemSelectedHasChanged:
301 for child in [self.sizer, self.buttonItemAddInput, self.buttonItemAddOutput]: 304 for child in [self.sizer, self.buttonItemAddInput, self.buttonItemAddOutput]:
302 child.setVisible(value) 305 child.setVisible(value)
303 return super(BlockItem, self).itemChange(change, value) 306 return super(BlockItem, self).itemChange(change, value)
304 def myDelete(self): 307 def myDelete(self):
305 # TODO: move the below to blockitem:
306 scene = self.scene() 308 scene = self.scene()
307 if scene: 309 if scene:
308 for p in self.inputs + self.outputs: 310 for p in self.inputs + self.outputs:
309 if p.connection: 311 if p.connection:
310 p.connection.myDelete() 312 p.connection.myDelete()
311 scene.removeItem(self) 313 scene.removeItem(self)
312
313 def updateSize(self): 314 def updateSize(self):
314 rect = self.rect() 315 rect = self.rect()
315 h, w = rect.height(), rect.width() 316 h, w = rect.height(), rect.width()
316 self.buttonItemAddInput.setPos(0, h + 4) 317 self.buttonItemAddInput.setPos(0, h + 4)
317 self.buttonItemAddOutput.setPos(w+10, h+4) 318 self.buttonItemAddOutput.setPos(w+10, h+4)
318 for inp in self.inputs: 319 for inp, y in zip(self.inputs, equalSpace(len(self.inputs), h)):
319 inp.setX(0.0) 320 inp.setPos(0.0, y)
320 for inp in self.outputs: 321 for outp, y in zip(self.outputs, equalSpace(len(self.outputs), h)):
321 inp.setX(w) 322 outp.setPos(w, y)
322 if len(self.inputs) == 1:
323 self.inputs[0].setPos(0.0, h / 2)
324 elif len(self.inputs) > 1:
325 y = 15
326 dy = (h - 30) / (len(self.inputs) - 1)
327 for inp in self.inputs:
328 inp.setPos(0.0, y)
329 y += dy
330 if len(self.outputs) == 1:
331 self.outputs[0].setPos(w, h / 2)
332 elif len(self.outputs) > 1:
333 y = 15
334 dy = (h - 30) / (len(self.outputs) - 1)
335 for outp in self.outputs:
336 outp.setPos(w, y)
337 y += dy
338
339 def changeSize(self, p): 323 def changeSize(self, p):
340 """ Resize block function """ 324 h = 20 if p.y() < 20 else p.y()
341 w, h = p.x(), p.y() 325 w = 40 if p.x() < 40 else p.x()
342 # Limit the block size:
343 h = 20 if h < 20 else h
344 w = 40 if w < 40 else w
345 self.setRect(0.0, 0.0, w, h) 326 self.setRect(0.0, 0.0, w, h)
346 # center label:
347 rect = self.label.boundingRect() 327 rect = self.label.boundingRect()
348 lw, lh = rect.width(), rect.height() 328 self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2)
349 lx = (w - lw) / 2
350 ly = (h - lh) / 2
351 self.label.setPos(lx, ly)
352 # Update port positions:
353 self.updateSize() 329 self.updateSize()
354 return QPointF(w, h) 330 return QPointF(w, h)
355 331
356 class EditorGraphicsView(QGraphicsView): 332 class EditorGraphicsView(QGraphicsView):
357 def __init__(self, parent=None): 333 def __init__(self, parent=None):
375 rect = self.diagramScene.itemsBoundingRect() 351 rect = self.diagramScene.itemsBoundingRect()
376 self.fitInView(rect, Qt.KeepAspectRatio) 352 self.fitInView(rect, Qt.KeepAspectRatio)
377 def test(self): 353 def test(self):
378 self.rotate(30) 354 self.rotate(30)
379 self.zoomAll() 355 self.zoomAll()
380
381 def wheelEvent(self, event): 356 def wheelEvent(self, event):
382 pos = event.pos() 357 pos = event.pos()
383 posbefore = self.mapToScene(pos) 358 posbefore = self.mapToScene(pos)
384 degrees = event.delta() / 8.0 359 degrees = event.delta() / 8.0
385 sx = (100.0 + degrees) / 100.0 360 sx = (100.0 + degrees) / 100.0
413 class DiagramScene(QGraphicsScene): 388 class DiagramScene(QGraphicsScene):
414 """ Save and load and deletion of item""" 389 """ Save and load and deletion of item"""
415 def __init__(self, parent=None): 390 def __init__(self, parent=None):
416 super(DiagramScene, self).__init__(parent) 391 super(DiagramScene, self).__init__(parent)
417 self.startedConnection = None 392 self.startedConnection = None
418
419 def saveDiagram(self, filename): 393 def saveDiagram(self, filename):
420 items = self.items() 394 items = self.items()
421 blocks = [item for item in items if type(item) is BlockItem] 395 blocks = [item for item in items if type(item) is BlockItem]
422 connections = [item for item in items if type(item) is Connection] 396 connections = [item for item in items if type(item) is Connection]
423
424 doc = md.Document() 397 doc = md.Document()
425 modelElement = doc.createElement('system') 398 modelElement = doc.createElement('system')
426 doc.appendChild(modelElement) 399 doc.appendChild(modelElement)
427 for block in blocks: 400 for block in blocks:
428 blockElement = doc.createElement("block") 401 blockElement = doc.createElement("block")
538 pos = event.scenePos() 511 pos = event.scenePos()
539 self.startedConnection.setEndPos(pos) 512 self.startedConnection.setEndPos(pos)
540 super(DiagramScene, self).mouseMoveEvent(event) 513 super(DiagramScene, self).mouseMoveEvent(event)
541 def mouseReleaseEvent(self, event): 514 def mouseReleaseEvent(self, event):
542 if self.startedConnection: 515 if self.startedConnection:
543 items = self.items(event.scenePos()) 516 for item in self.items(event.scenePos()):
544 for item in items:
545 if type(item) is PortItem: 517 if type(item) is PortItem:
546 self.startedConnection.setToPort(item) 518 if item.direction == 'input' and item.connection == None:
547 self.startedConnection = None 519 self.startedConnection.setToPort(item)
548 return 520 self.startedConnection = None
521 return
549 self.startedConnection.myDelete() 522 self.startedConnection.myDelete()
550 self.startedConnection = None 523 self.startedConnection = None
551 super(DiagramScene, self).mouseReleaseEvent(event) 524 super(DiagramScene, self).mouseReleaseEvent(event)
552 def startConnection(self, port): 525 def startConnection(self, port):
553 self.startedConnection = Connection(port, None) 526 self.startedConnection = Connection(port, None)
554 pos = port.scenePos() 527 pos = port.scenePos()
555 self.startedConnection.setEndPos(pos) 528 self.startedConnection.setEndPos(pos)
556 self.addItem(self.startedConnection) 529 self.addItem(self.startedConnection)
557 def deleteItems(self): 530 def deleteItems(self):
558 # Try to delete all currently selected items:
559 for item in list(self.selectedItems()): 531 for item in list(self.selectedItems()):
560 item.myDelete() 532 item.myDelete()
561 533
562 class LibraryWidget(QListView): 534 class LibraryWidget(QListView):
563 def __init__(self): 535 def __init__(self):
598 toolbar.addAction(a) 570 toolbar.addAction(a)
599 act('Save', QKeySequence.Save, self.editor.save) 571 act('Save', QKeySequence.Save, self.editor.save)
600 act('Load', QKeySequence.Open, self.editor.load) 572 act('Load', QKeySequence.Open, self.editor.load)
601 act('Full screen', QKeySequence("F11"), self.toggleFullScreen) 573 act('Full screen', QKeySequence("F11"), self.toggleFullScreen)
602 act('Fit in view', QKeySequence("F8"), self.editor.zoomAll) 574 act('Fit in view', QKeySequence("F8"), self.editor.zoomAll)
603
604 self.library = LibraryWidget() 575 self.library = LibraryWidget()
605 libraryDock = QDockWidget('Library', self) 576 libraryDock = QDockWidget('Library', self)
606 libraryDock.setWidget(self.library) 577 libraryDock.setWidget(self.library)
607 self.addDockWidget(Qt.LeftDockWidgetArea, libraryDock) 578 self.addDockWidget(Qt.LeftDockWidgetArea, libraryDock)
608
609 self.editor.loadDiagram('diagram2.usd') 579 self.editor.loadDiagram('diagram2.usd')
610
611 def toggleFullScreen(self): 580 def toggleFullScreen(self):
612 self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) 581 self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
613 self.editor.zoomAll() 582 self.editor.zoomAll()
614 583
615 if __name__ == '__main__': 584 if __name__ == '__main__':
616 if sys.version_info.major != 3: 585 if sys.version_info.major != 3:
617 print('Please use python 3.x') 586 print('Please use python 3.x')
618 sys.exit(1) 587 sys.exit(1)
619
620 app = QApplication(sys.argv) 588 app = QApplication(sys.argv)
621 main = Main() 589 main = Main()
622 main.show() 590 main.show()
623 main.resize(700, 500) 591 main.resize(700, 500)
624 main.editor.zoomAll() 592 main.editor.zoomAll()