Mercurial > lcfOS
comparison applications/lab/diagrameditor.py @ 54:d8163d2c3779
Rework of delete function.
author | windel |
---|---|
date | Thu, 12 Apr 2012 18:03:33 +0200 |
parents | 6dd7d6a1737c |
children | 9414bad225a8 |
comparison
equal
deleted
inserted
replaced
53:6dd7d6a1737c | 54:d8163d2c3779 |
---|---|
38 #def shape(self): | 38 #def shape(self): |
39 # return self.path() | 39 # return self.path() |
40 def setFromPort(self, fromPort): | 40 def setFromPort(self, fromPort): |
41 if self.fromPort: | 41 if self.fromPort: |
42 self.fromPort.posCallbacks.remove(self.setBeginPos) | 42 self.fromPort.posCallbacks.remove(self.setBeginPos) |
43 self.fromPort.connection = None | |
43 self.fromPort = fromPort | 44 self.fromPort = fromPort |
44 if self.fromPort: | 45 if self.fromPort: |
46 self.fromPort.connection = self | |
45 self.setBeginPos(fromPort.scenePos()) | 47 self.setBeginPos(fromPort.scenePos()) |
46 self.fromPort.posCallbacks.append(self.setBeginPos) | 48 self.fromPort.posCallbacks.append(self.setBeginPos) |
47 def setToPort(self, toPort): | 49 def setToPort(self, toPort): |
48 if self.toPort: | 50 if self.toPort: |
49 self.toPort.posCallbacks.remove(self.setEndPos) | 51 self.toPort.posCallbacks.remove(self.setEndPos) |
52 self.toPort.connection = None | |
50 self.toPort = toPort | 53 self.toPort = toPort |
51 if self.toPort: | 54 if self.toPort: |
52 self.setEndPos(toPort.scenePos()) | 55 self.setEndPos(toPort.scenePos()) |
56 self.toPort.connection = self | |
53 self.toPort.posCallbacks.append(self.setEndPos) | 57 self.toPort.posCallbacks.append(self.setEndPos) |
54 def releasePorts(self): | 58 def releasePorts(self): |
55 self.setFromPort(None) | 59 self.setFromPort(None) |
56 self.setToPort(None) | 60 self.setToPort(None) |
57 def setBeginPos(self, pos1): | 61 def setBeginPos(self, pos1): |
129 path.lineTo(p4) | 133 path.lineTo(p4) |
130 | 134 |
131 hits = stripHits(ds.items(path)) | 135 hits = stripHits(ds.items(path)) |
132 self.setPath(path) | 136 self.setPath(path) |
133 | 137 |
134 def delete(self): | |
135 editor.diagramScene.removeItem(self) | |
136 # Remove position update callbacks: | |
137 | |
138 class ParameterDialog(QDialog): | 138 class ParameterDialog(QDialog): |
139 def __init__(self, block, parent = None): | 139 def __init__(self, block, parent = None): |
140 super(ParameterDialog, self).__init__(parent) | 140 super(ParameterDialog, self).__init__(parent) |
141 self.block = block | 141 self.block = block |
142 self.button = QPushButton('Ok', self) | 142 self.button = QPushButton('Ok', self) |
171 class PortItem(QGraphicsPathItem): | 171 class PortItem(QGraphicsPathItem): |
172 """ Represents a port to a subsystem """ | 172 """ Represents a port to a subsystem """ |
173 def __init__(self, name, block, direction): | 173 def __init__(self, name, block, direction): |
174 super(PortItem, self).__init__(block) | 174 super(PortItem, self).__init__(block) |
175 #QRectF(-6,-6,12.0,12.0) | 175 #QRectF(-6,-6,12.0,12.0) |
176 self.connection = None | |
176 path = QPainterPath() | 177 path = QPainterPath() |
177 if direction == 'input': | 178 if direction == 'input': |
178 d = 5.0 | 179 d = 5.0 |
179 path.moveTo(-d, -d) | 180 path.moveTo(-d, -d) |
180 path.lineTo(0.0, 0.0) | 181 path.lineTo(0.0, 0.0) |
210 for cb in self.posCallbacks: | 211 for cb in self.posCallbacks: |
211 cb(value) | 212 cb(value) |
212 return value | 213 return value |
213 return super(PortItem, self).itemChange(change, value) | 214 return super(PortItem, self).itemChange(change, value) |
214 def mousePressEvent(self, event): | 215 def mousePressEvent(self, event): |
215 editor.startConnection(self) | 216 if self.direction == 'output': |
217 editor.startConnection(self) | |
216 | 218 |
217 class OutputPort(PortItem): | 219 class OutputPort(PortItem): |
218 # TODO: create a subclass OR make a member porttype | 220 # TODO: create a subclass OR make a member porttype |
219 pass | 221 pass |
220 | 222 |
223 """ A handle that can be moved by the mouse """ | 225 """ A handle that can be moved by the mouse """ |
224 def __init__(self, parent=None): | 226 def __init__(self, parent=None): |
225 super(HandleItem, self).__init__(QRectF(-4.0,-4.0,8.0,8.0), parent) | 227 super(HandleItem, self).__init__(QRectF(-4.0,-4.0,8.0,8.0), parent) |
226 self.posChangeCallbacks = [] | 228 self.posChangeCallbacks = [] |
227 self.setBrush(QBrush(Qt.white)) | 229 self.setBrush(QBrush(Qt.white)) |
228 self.setFlags(self.ItemIsMovable | self.ItemSendsScenePositionChanges) | 230 self.setFlag(self.ItemSendsScenePositionChanges, True) |
231 self.setFlag(self.ItemIsMovable, True) | |
232 #self.setFlag(self.ItemIsSelectable, False) | |
229 self.setCursor(QCursor(Qt.SizeFDiagCursor)) | 233 self.setCursor(QCursor(Qt.SizeFDiagCursor)) |
230 | 234 |
231 def itemChange(self, change, value): | 235 def itemChange(self, change, value): |
232 if change == self.ItemPositionChange: | 236 if change == self.ItemPositionChange: |
233 #value = value.toPointF() | |
234 x, y = value.x(), value.y() | 237 x, y = value.x(), value.y() |
235 # TODO: make this a signal? | |
236 # This cannot be a signal because this is not a QObject | |
237 for cb in self.posChangeCallbacks: | 238 for cb in self.posChangeCallbacks: |
238 res = cb(x, y) | 239 res = cb(x, y) |
239 if res: | 240 if res: |
240 x, y = res | 241 x, y = res |
241 value = QPointF(x, y) | 242 value = QPointF(x, y) |
257 super(BlockItem, self).__init__(parent) | 258 super(BlockItem, self).__init__(parent) |
258 # Properties of the rectangle: | 259 # Properties of the rectangle: |
259 self.setPen(QPen(Qt.blue, 2)) | 260 self.setPen(QPen(Qt.blue, 2)) |
260 self.setBrush(QBrush(Qt.lightGray)) | 261 self.setBrush(QBrush(Qt.lightGray)) |
261 self.setFlags(self.ItemIsSelectable | self.ItemIsMovable) | 262 self.setFlags(self.ItemIsSelectable | self.ItemIsMovable) |
263 self.setBoundingRegionGranularity(1) | |
262 self.setCursor(QCursor(Qt.PointingHandCursor)) | 264 self.setCursor(QCursor(Qt.PointingHandCursor)) |
263 self.label = QGraphicsTextItem(name, self) | 265 self.label = QGraphicsTextItem(name, self) |
264 self.name = name | 266 self.name = name |
265 # Create corner for resize: | 267 # Create corner for resize: |
266 self.sizer = HandleItem(self) | 268 self.sizer = HandleItem(self) |
267 self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback | 269 self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback |
268 #self.sizer.setVisible(False) | 270 #self.sizer.setVisible(False) |
269 self.sizer.setFlag(self.sizer.ItemIsSelectable, True) | |
270 | 271 |
271 # Inputs and outputs of the block: | 272 # Inputs and outputs of the block: |
272 self.inputs = [] | 273 self.inputs = [] |
273 self.outputs = [] | 274 self.outputs = [] |
274 # Update size: | 275 # Update size: |
275 self.changeSize(60, 40) | 276 self.sizer.setPos(60, 40) # This is a better resize function |
277 #self.changeSize(60, 40) # TODO: create a wrapper function | |
276 def editParameters(self): | 278 def editParameters(self): |
277 pd = ParameterDialog(self, self.window()) | 279 pd = ParameterDialog(self, self.window()) |
278 pd.exec_() | 280 pd.exec_() |
279 def addPort(self): | 281 def addPort(self): |
280 pd = AddPortDialog(self, self.window()) | 282 pd = AddPortDialog(self, self.window()) |
289 self.outputs.append(o) | 291 self.outputs.append(o) |
290 self.updateSize() | 292 self.updateSize() |
291 | 293 |
292 def contextMenuEvent(self, event): | 294 def contextMenuEvent(self, event): |
293 menu = QMenu() | 295 menu = QMenu() |
294 da = menu.addAction('Delete') | |
295 da.triggered.connect(self.delete) | |
296 pa = menu.addAction('Parameters') | 296 pa = menu.addAction('Parameters') |
297 pa.triggered.connect(self.editParameters) | 297 pa.triggered.connect(self.editParameters) |
298 pa = menu.addAction('Add port') | 298 pa = menu.addAction('Add port') |
299 pa.triggered.connect(self.addPort) | 299 pa.triggered.connect(self.addPort) |
300 menu.exec_(event.screenPos()) | 300 menu.exec_(event.screenPos()) |
301 | |
302 def delete(self): | |
303 editor.diagramScene.removeItem(self) | |
304 # TODO: remove connections | |
305 | 301 |
306 def updateSize(self): | 302 def updateSize(self): |
307 rect = self.rect() | 303 rect = self.rect() |
308 h, w = rect.height(), rect.width() | 304 h, w = rect.height(), rect.width() |
309 if len(self.inputs) == 1: | 305 if len(self.inputs) == 1: |
342 return w, h | 338 return w, h |
343 | 339 |
344 class EditorGraphicsView(QGraphicsView): | 340 class EditorGraphicsView(QGraphicsView): |
345 def __init__(self, scene, parent=None): | 341 def __init__(self, scene, parent=None): |
346 QGraphicsView.__init__(self, scene, parent) | 342 QGraphicsView.__init__(self, scene, parent) |
343 self.setDragMode(QGraphicsView.RubberBandDrag) | |
347 def dragEnterEvent(self, event): | 344 def dragEnterEvent(self, event): |
348 if event.mimeData().hasFormat('component/name'): | 345 if event.mimeData().hasFormat('component/name'): |
349 event.accept() | 346 event.accept() |
350 def dragMoveEvent(self, event): | 347 def dragMoveEvent(self, event): |
351 if event.mimeData().hasFormat('component/name'): | 348 if event.mimeData().hasFormat('component/name'): |
369 txt = self.data(idx, Qt.DisplayRole) # python 3 | 366 txt = self.data(idx, Qt.DisplayRole) # python 3 |
370 mimedata.setData('component/name', txt) | 367 mimedata.setData('component/name', txt) |
371 return mimedata | 368 return mimedata |
372 | 369 |
373 class DiagramScene(QGraphicsScene): | 370 class DiagramScene(QGraphicsScene): |
371 """ Save and load and deletion of item""" | |
374 def __init__(self, parent=None): | 372 def __init__(self, parent=None): |
375 super(DiagramScene, self).__init__(parent) | 373 super(DiagramScene, self).__init__(parent) |
376 def mouseMoveEvent(self, mouseEvent): | |
377 editor.sceneMouseMoveEvent(mouseEvent) | |
378 super(DiagramScene, self).mouseMoveEvent(mouseEvent) | |
379 def mouseReleaseEvent(self, mouseEvent): | |
380 editor.sceneMouseReleaseEvent(mouseEvent) | |
381 super(DiagramScene, self).mouseReleaseEvent(mouseEvent) | |
382 | |
383 class DiagramEditor(QWidget): | |
384 def __init__(self, parent=None): | |
385 QWidget.__init__(self, parent) | |
386 self.setWindowTitle("Diagram editor") | |
387 | |
388 # Widget layout and child widgets: | |
389 self.horizontalLayout = QHBoxLayout(self) | |
390 self.libraryBrowserView = QListView(self) | |
391 self.libraryBrowserView.setMaximumWidth(160) | |
392 self.libraryModel = LibraryModel(self) | |
393 self.libraryModel.setColumnCount(1) | |
394 # Create an icon with an icon: | |
395 pixmap = QPixmap(60, 60) | |
396 pixmap.fill() | |
397 painter = QPainter(pixmap) | |
398 painter.fillRect(10, 10, 40, 40, Qt.blue) | |
399 painter.setBrush(Qt.red) | |
400 painter.drawEllipse(36, 2, 20, 20) | |
401 painter.setBrush(Qt.yellow) | |
402 painter.drawEllipse(20, 20, 20, 20) | |
403 painter.end() | |
404 | |
405 # Fill library: | |
406 self.libItems = [] | |
407 self.libItems.append( QStandardItem(QIcon(pixmap), 'Block') ) | |
408 self.libItems.append( QStandardItem(QIcon(pixmap), 'Uber Unit') ) | |
409 self.libItems.append( QStandardItem(QIcon(pixmap), 'Device') ) | |
410 for i in self.libItems: | |
411 self.libraryModel.appendRow(i) | |
412 self.libraryBrowserView.setModel(self.libraryModel) | |
413 self.libraryBrowserView.setViewMode(self.libraryBrowserView.IconMode) | |
414 self.libraryBrowserView.setDragDropMode(self.libraryBrowserView.DragOnly) | |
415 | |
416 self.diagramScene = DiagramScene(self) | |
417 self.diagramView = EditorGraphicsView(self.diagramScene, self) | |
418 self.horizontalLayout.addWidget(self.libraryBrowserView) | |
419 self.horizontalLayout.addWidget(self.diagramView) | |
420 | |
421 self.startedConnection = None | |
422 fullScreenShortcut = QShortcut(QKeySequence("F11"), self) | |
423 fullScreenShortcut.activated.connect(self.toggleFullScreen) | |
424 saveShortcut = QShortcut(QKeySequence(QKeySequence.Save), self) | |
425 saveShortcut.activated.connect(self.save) | |
426 testShortcut = QShortcut(QKeySequence("F12"), self) | |
427 testShortcut.activated.connect(self.test) | |
428 zoomShortcut = QShortcut(QKeySequence("F8"), self) | |
429 zoomShortcut.activated.connect(self.zoomAll) | |
430 delShort = QShortcut(QKeySequence.Delete, self) | |
431 delShort.activated.connect(self.deleteItem) | |
432 | |
433 def test(self): | |
434 self.diagramView.rotate(11) | |
435 def save(self): | |
436 self.saveDiagram('diagram2.usd') | |
437 | |
438 def saveDiagram(self, filename): | 374 def saveDiagram(self, filename): |
439 items = self.diagramScene.items() | 375 items = self.items() |
440 blocks = [item for item in items if type(item) is BlockItem] | 376 blocks = [item for item in items if type(item) is BlockItem] |
441 connections = [item for item in items if type(item) is Connection] | 377 connections = [item for item in items if type(item) is Connection] |
442 | 378 |
443 doc = md.Document() | 379 doc = md.Document() |
444 modelElement = doc.createElement('system') | 380 modelElement = doc.createElement('system') |
491 y = float(blockElement.getAttribute('y')) | 427 y = float(blockElement.getAttribute('y')) |
492 w = float(blockElement.getAttribute('width')) | 428 w = float(blockElement.getAttribute('width')) |
493 h = float(blockElement.getAttribute('height')) | 429 h = float(blockElement.getAttribute('height')) |
494 name = blockElement.getAttribute('name') | 430 name = blockElement.getAttribute('name') |
495 block = BlockItem(name) | 431 block = BlockItem(name) |
496 self.diagramScene.addItem(block) | 432 self.addItem(block) |
497 block.setPos(x, y) | 433 block.setPos(x, y) |
498 block.sizer.setPos(w, h) | 434 block.sizer.setPos(w, h) |
499 # Load ports: | 435 # Load ports: |
500 portElements = blockElement.getElementsByTagName('input') | 436 portElements = blockElement.getElementsByTagName('input') |
501 for portElement in portElements: | 437 for portElement in portElements: |
514 toBlock = connectionElement.getAttribute('toBlock') | 450 toBlock = connectionElement.getAttribute('toBlock') |
515 toPort = connectionElement.getAttribute('toPort') | 451 toPort = connectionElement.getAttribute('toPort') |
516 fromPort = self.findPort(fromBlock, fromPort) | 452 fromPort = self.findPort(fromBlock, fromPort) |
517 toPort = self.findPort(toBlock, toPort) | 453 toPort = self.findPort(toBlock, toPort) |
518 connection = Connection(fromPort, toPort) | 454 connection = Connection(fromPort, toPort) |
519 self.diagramScene.addItem(connection) | 455 self.addItem(connection) |
520 | |
521 self.zoomAll() | |
522 | |
523 def findPort(self, blockname, portname): | 456 def findPort(self, blockname, portname): |
524 items = self.diagramScene.items() | 457 items = self.items() |
525 blocks = [item for item in items if type(item) is BlockItem] | 458 blocks = [item for item in items if type(item) is BlockItem] |
526 for block in [b for b in blocks if b.name == blockname]: | 459 for block in [b for b in blocks if b.name == blockname]: |
527 for port in block.inputs + block.outputs: | 460 for port in block.inputs + block.outputs: |
528 if port.name == portname: | 461 if port.name == portname: |
529 return port | 462 return port |
530 | 463 def mouseMoveEvent(self, mouseEvent): |
464 editor.sceneMouseMoveEvent(mouseEvent) | |
465 super(DiagramScene, self).mouseMoveEvent(mouseEvent) | |
466 def mouseReleaseEvent(self, mouseEvent): | |
467 editor.sceneMouseReleaseEvent(mouseEvent) | |
468 super(DiagramScene, self).mouseReleaseEvent(mouseEvent) | |
469 def deleteItem(self, item=None): | |
470 if item: | |
471 if type(item) is BlockItem: | |
472 for p in item.inputs + item.outputs: | |
473 if p.connection: | |
474 self.deleteItem(p.connection) | |
475 self.removeItem(item) | |
476 elif type(item) is Connection: | |
477 item.releasePorts() | |
478 self.removeItem(item) | |
479 else: | |
480 # No item was supplied, try to delete all currently selected items: | |
481 items = self.selectedItems() | |
482 connections = [item for item in items if type(item) is Connection] | |
483 blocks = [item for item in items if type(item) is BlockItem] | |
484 for item in connections + blocks: | |
485 self.deleteItem(item) | |
486 | |
487 class DiagramEditor(QWidget): | |
488 def __init__(self, parent=None): | |
489 QWidget.__init__(self, parent) | |
490 self.setWindowTitle("Diagram editor") | |
491 | |
492 # Widget layout and child widgets: | |
493 self.horizontalLayout = QHBoxLayout(self) | |
494 self.libraryBrowserView = QListView(self) | |
495 self.libraryBrowserView.setMaximumWidth(160) | |
496 self.libraryModel = LibraryModel(self) | |
497 self.libraryModel.setColumnCount(1) | |
498 # Create an icon with an icon: | |
499 pixmap = QPixmap(60, 60) | |
500 pixmap.fill() | |
501 painter = QPainter(pixmap) | |
502 painter.fillRect(10, 10, 40, 40, Qt.blue) | |
503 painter.setBrush(Qt.red) | |
504 painter.drawEllipse(36, 2, 20, 20) | |
505 painter.setBrush(Qt.yellow) | |
506 painter.drawEllipse(20, 20, 20, 20) | |
507 painter.end() | |
508 | |
509 # Fill library: | |
510 self.libItems = [] | |
511 self.libItems.append( QStandardItem(QIcon(pixmap), 'Block') ) | |
512 self.libItems.append( QStandardItem(QIcon(pixmap), 'Uber Unit') ) | |
513 self.libItems.append( QStandardItem(QIcon(pixmap), 'Device') ) | |
514 for i in self.libItems: | |
515 self.libraryModel.appendRow(i) | |
516 self.libraryBrowserView.setModel(self.libraryModel) | |
517 self.libraryBrowserView.setViewMode(self.libraryBrowserView.IconMode) | |
518 self.libraryBrowserView.setDragDropMode(self.libraryBrowserView.DragOnly) | |
519 | |
520 self.diagramScene = DiagramScene(self) | |
521 self.loadDiagram = self.diagramScene.loadDiagram | |
522 self.diagramView = EditorGraphicsView(self.diagramScene, self) | |
523 self.horizontalLayout.addWidget(self.libraryBrowserView) | |
524 self.horizontalLayout.addWidget(self.diagramView) | |
525 | |
526 self.startedConnection = None | |
527 fullScreenShortcut = QShortcut(QKeySequence("F11"), self) | |
528 fullScreenShortcut.activated.connect(self.toggleFullScreen) | |
529 saveShortcut = QShortcut(QKeySequence(QKeySequence.Save), self) | |
530 saveShortcut.activated.connect(self.save) | |
531 testShortcut = QShortcut(QKeySequence("F12"), self) | |
532 testShortcut.activated.connect(self.test) | |
533 zoomShortcut = QShortcut(QKeySequence("F8"), self) | |
534 zoomShortcut.activated.connect(self.zoomAll) | |
535 delShort = QShortcut(QKeySequence.Delete, self) | |
536 delShort.activated.connect(self.diagramScene.deleteItem) | |
537 | |
538 def test(self): | |
539 self.diagramView.rotate(30) | |
540 self.zoomAll() | |
541 def save(self): | |
542 self.diagramScene.saveDiagram('diagram2.usd') | |
531 def toggleFullScreen(self): | 543 def toggleFullScreen(self): |
532 self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) | 544 self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) |
533 def startConnection(self, port): | 545 def startConnection(self, port): |
534 self.startedConnection = Connection(port, None) | 546 self.startedConnection = Connection(port, None) |
535 self.diagramScene.addItem(self.startedConnection) | 547 self.diagramScene.addItem(self.startedConnection) |
536 def zoomAll(self): | 548 def zoomAll(self): |
537 """ zoom to fit all items """ | 549 """ zoom to fit all items """ |
538 rect = self.diagramScene.itemsBoundingRect() | 550 rect = self.diagramScene.itemsBoundingRect() |
539 self.diagramView.fitInView(rect, Qt.KeepAspectRatio) | 551 self.diagramView.fitInView(rect, Qt.KeepAspectRatio) |
540 def deleteItem(self): | |
541 items = self.diagramScene.selectedItems() | |
542 for item in items: | |
543 if type(item) is Connection: | |
544 item.releasePorts() | |
545 item.delete() | |
546 if type(item) is Block: | |
547 pass | |
548 | |
549 def sceneMouseMoveEvent(self, event): | 552 def sceneMouseMoveEvent(self, event): |
550 if self.startedConnection: | 553 if self.startedConnection: |
551 pos = event.scenePos() | 554 pos = event.scenePos() |
552 self.startedConnection.setEndPos(pos) | 555 self.startedConnection.setEndPos(pos) |
553 def sceneMouseReleaseEvent(self, event): | 556 def sceneMouseReleaseEvent(self, event): |
557 for item in items: | 560 for item in items: |
558 if type(item) is PortItem: | 561 if type(item) is PortItem: |
559 self.startedConnection.setToPort(item) | 562 self.startedConnection.setToPort(item) |
560 self.startedConnection = None | 563 self.startedConnection = None |
561 return | 564 return |
562 self.startedConnection.delete() | 565 self.diagramScene.deleteItem(self.startedConnection) |
563 del self.startedConnection | 566 del self.startedConnection |
564 self.startedConnection = None | 567 self.startedConnection = None |
565 | 568 |
566 if __name__ == '__main__': | 569 if __name__ == '__main__': |
567 if sys.version_info.major != 3: | 570 if sys.version_info.major != 3: |