Mercurial > lcfOS
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() |