comparison applications/lab/diagrameditor.py @ 49:2b07ab2c0dc4

Added line saving
author windel
date Wed, 04 Apr 2012 07:30:21 +0200
parents a309982ab4d5
children 38ff8e178fe4
comparison
equal deleted inserted replaced
48:a309982ab4d5 49:2b07ab2c0dc4
7 7
8 """ 8 """
9 This script implements a basic diagram editor. 9 This script implements a basic diagram editor.
10 """ 10 """
11 11
12 class Connection: 12 class Connection(QGraphicsPathItem):
13 """ 13 """
14 - fromPort 14 - fromPort
15 - list of line items in between 15 - list of line items in between
16 - toPort 16 - toPort
17 """ 17 """
18 def __init__(self, fromPort, toPort): 18 def __init__(self, fromPort, toPort):
19 super(Connection, self).__init__()
19 self.pos1 = None 20 self.pos1 = None
20 self.pos2 = None 21 self.pos2 = None
21 self.pathItem = QGraphicsPathItem()
22 pen = QPen() 22 pen = QPen()
23 pen.setWidth(4) 23 pen.setWidth(2)
24 pen.setColor(Qt.blue) 24 pen.setColor(Qt.blue)
25 pen.setCapStyle(Qt.RoundCap) 25 pen.setCapStyle(Qt.RoundCap)
26 self.pathItem.setPen(pen) 26 self.setPen(pen)
27 #self.pathItem.setFlags(self.pathItem.ItemIsSelectable) 27 editor.diagramScene.addItem(self)
28 editor.diagramScene.addItem(self.pathItem)
29 self.setFromPort(fromPort) 28 self.setFromPort(fromPort)
30 self.setToPort(toPort) 29 self.setToPort(toPort)
31 def setFromPort(self, fromPort): 30 def setFromPort(self, fromPort):
32 self.fromPort = fromPort 31 self.fromPort = fromPort
33 if self.fromPort: 32 if self.fromPort:
63 x4, y4 = self.pos2.x(), self.pos2.y() 62 x4, y4 = self.pos2.x(), self.pos2.y()
64 63
65 def stripHits(hits): 64 def stripHits(hits):
66 """ Helper function that removes object hits """ 65 """ Helper function that removes object hits """
67 hits = [hit for hit in hits if type(hit) is BlockItem] 66 hits = [hit for hit in hits if type(hit) is BlockItem]
68 if self.pathItem in hits: 67 if self in hits:
69 hits.remove(self.pathItem) 68 hits.remove(self)
70 if self.fromPort in hits: 69 if self.fromPort in hits:
71 hits.remove(self.fromPort) 70 hits.remove(self.fromPort)
72 if self.toPort in hits: 71 if self.toPort in hits:
73 hits.remove(self.toPort) 72 hits.remove(self.toPort)
74 return hits 73 return hits
110 path.lineTo(p4) 109 path.lineTo(p4)
111 110
112 hits = stripHits(ds.items(path)) 111 hits = stripHits(ds.items(path))
113 112
114 #print('Items:', hits) 113 #print('Items:', hits)
115 self.pathItem.setPath(path) 114 self.setPath(path)
116 115
117 def delete(self): 116 def delete(self):
118 editor.diagramScene.removeItem(self.pathItem) 117 editor.diagramScene.removeItem(self)
119 # Remove position update callbacks: 118 # Remove position update callbacks:
120 119
121 class ParameterDialog(QDialog): 120 class ParameterDialog(QDialog):
122 def __init__(self, parent = None): 121 def __init__(self, block, parent = None):
123 super(ParameterDialog, self).__init__(parent) 122 super(ParameterDialog, self).__init__(parent)
123 self.block = block
124 self.button = QPushButton('Ok', self) 124 self.button = QPushButton('Ok', self)
125 l = QVBoxLayout(self) 125 l = QGridLayout(self)
126 l.addWidget(self.button) 126 l.addWidget(QLabel('Name:', self), 0, 0)
127 self.nameEdit = QLineEdit(self.block.label.toPlainText())
128 l.addWidget(self.nameEdit, 0, 1)
129 l.addWidget(self.button, 1, 0)
127 self.button.clicked.connect(self.OK) 130 self.button.clicked.connect(self.OK)
128 def OK(self): 131 def OK(self):
132 self.block.label.setPlainText(self.nameEdit.text())
129 self.close() 133 self.close()
130 134
131 class PortItem(QGraphicsEllipseItem): 135 class PortItem(QGraphicsEllipseItem):
132 """ Represents a port to a subsystem """ 136 """ Represents a port to a subsystem """
133 def __init__(self, name, parent=None): 137 def __init__(self, name, parent=None):
201 #self.sizer.setVisible(False) 205 #self.sizer.setVisible(False)
202 self.sizer.setFlag(self.sizer.ItemIsSelectable, True) 206 self.sizer.setFlag(self.sizer.ItemIsSelectable, True)
203 207
204 # Inputs and outputs of the block: 208 # Inputs and outputs of the block:
205 self.inputs = [] 209 self.inputs = []
206 self.inputs.append( PortItem('a', self) ) 210 #self.inputs.append( PortItem('a', self) )
207 self.inputs.append( PortItem('b', self) ) 211 #self.inputs.append( PortItem('b', self) )
208 self.inputs.append( PortItem('c', self) ) 212 #self.inputs.append( PortItem('c', self) )
209 self.outputs = [] 213 self.outputs = []
210 self.outputs.append( PortItem('y', self) ) 214 #self.outputs.append( PortItem('y', self) )
211 # Update size: 215 # Update size:
212 self.changeSize(w, h) 216 self.changeSize(w, h)
213 def editParameters(self): 217 def editParameters(self):
214 pd = ParameterDialog(self.window()) 218 pd = ParameterDialog(self, self.window())
215 pd.exec_() 219 pd.exec_()
216 def addInput(self, i): 220 def addInput(self, i):
217 self.inputs.append(i) 221 self.inputs.append(i)
222 self.updateSize()
218 def addOutput(self, o): 223 def addOutput(self, o):
219 self.outputs.append(o) 224 self.outputs.append(o)
225 self.updateSize()
220 226
221 def contextMenuEvent(self, event): 227 def contextMenuEvent(self, event):
222 menu = QMenu() 228 menu = QMenu()
223 da = menu.addAction('Delete') 229 da = menu.addAction('Delete')
224 da.triggered.connect(self.delete) 230 da.triggered.connect(self.delete)
228 234
229 def delete(self): 235 def delete(self):
230 editor.diagramScene.removeItem(self) 236 editor.diagramScene.removeItem(self)
231 # TODO: remove connections 237 # TODO: remove connections
232 238
233 def changeSize(self, w, h): 239 def updateSize(self):
234 """ Resize block function """ 240 rect = self.rect()
235 # Limit the block size: 241 h, w = rect.height(), rect.width()
236 if h < 20:
237 h = 20
238 if w < 40:
239 w = 40
240 self.setRect(0.0, 0.0, w, h)
241 # center label:
242 rect = self.label.boundingRect()
243 lw, lh = rect.width(), rect.height()
244 lx = (w - lw) / 2
245 ly = (h - lh) / 2
246 self.label.setPos(lx, ly)
247 # Update port positions:
248 if len(self.inputs) == 1: 242 if len(self.inputs) == 1:
249 self.inputs[0].setPos(-4, h / 2) 243 self.inputs[0].setPos(-4, h / 2)
250 elif len(self.inputs) > 1: 244 elif len(self.inputs) > 1:
251 y = 5 245 y = 5
252 dy = (h - 10) / (len(self.inputs) - 1) 246 dy = (h - 10) / (len(self.inputs) - 1)
259 y = 5 253 y = 5
260 dy = (h - 10) / (len(self.outputs) + 0) 254 dy = (h - 10) / (len(self.outputs) + 0)
261 for outp in self.outputs: 255 for outp in self.outputs:
262 outp.setPos(w+4, y) 256 outp.setPos(w+4, y)
263 y += dy 257 y += dy
258
259 def changeSize(self, w, h):
260 """ Resize block function """
261 # Limit the block size:
262 if h < 20:
263 h = 20
264 if w < 40:
265 w = 40
266 self.setRect(0.0, 0.0, w, h)
267 # center label:
268 rect = self.label.boundingRect()
269 lw, lh = rect.width(), rect.height()
270 lx = (w - lw) / 2
271 ly = (h - lh) / 2
272 self.label.setPos(lx, ly)
273 # Update port positions:
274 self.updateSize()
264 return w, h 275 return w, h
265 276
266 class EditorGraphicsView(QGraphicsView): 277 class EditorGraphicsView(QGraphicsView):
267 def __init__(self, scene, parent=None): 278 def __init__(self, scene, parent=None):
268 QGraphicsView.__init__(self, scene, parent) 279 QGraphicsView.__init__(self, scene, parent)
274 event.accept() 285 event.accept()
275 def dropEvent(self, event): 286 def dropEvent(self, event):
276 if event.mimeData().hasFormat('component/name'): 287 if event.mimeData().hasFormat('component/name'):
277 name = bytes(event.mimeData().data('component/name')).decode() 288 name = bytes(event.mimeData().data('component/name')).decode()
278 b1 = BlockItem(name) 289 b1 = BlockItem(name)
279 print(event.pos())
280 b1.setPos(self.mapToScene(event.pos())) 290 b1.setPos(self.mapToScene(event.pos()))
281 print(self.mapToScene(event.pos()))
282 self.scene().addItem(b1) 291 self.scene().addItem(b1)
283 292
284 class LibraryModel(QStandardItemModel): 293 class LibraryModel(QStandardItemModel):
285 def __init__(self, parent=None): 294 def __init__(self, parent=None):
286 QStandardItemModel.__init__(self, parent) 295 QStandardItemModel.__init__(self, parent)
359 self.saveDiagram('diagram2.usd') 368 self.saveDiagram('diagram2.usd')
360 369
361 def saveDiagram(self, filename): 370 def saveDiagram(self, filename):
362 items = self.diagramScene.items() 371 items = self.diagramScene.items()
363 blocks = [item for item in items if type(item) is BlockItem] 372 blocks = [item for item in items if type(item) is BlockItem]
373 connections = [item for item in items if type(item) is Connection]
364 374
365 doc = md.Document() 375 doc = md.Document()
366 modelElement = doc.createElement('system') 376 modelElement = doc.createElement('system')
367 doc.appendChild(modelElement) 377 doc.appendChild(modelElement)
368 for block in blocks: 378 for block in blocks:
369 blockElement = doc.createElement("block") 379 blockElement = doc.createElement("block")
370 x, y = block.scenePos().x(), block.scenePos().y() 380 x, y = block.scenePos().x(), block.scenePos().y()
371 rect = block.rect() 381 rect = block.rect()
372 w, h = rect.width(), rect.height() 382 w, h = rect.width(), rect.height()
373 blockElement.setAttribute("name", block.label.toPlainText()) 383 blockElement.setAttribute("name", block.label.toPlainText())
374 blockElement.setAttribute("x", str(x)) 384 blockElement.setAttribute("x", str(int(x)))
375 blockElement.setAttribute("y", str(y)) 385 blockElement.setAttribute("y", str(int(y)))
376 blockElement.setAttribute("width", str(w)) 386 blockElement.setAttribute("width", str(int(w)))
377 blockElement.setAttribute("height", str(h)) 387 blockElement.setAttribute("height", str(int(h)))
378 for inp in block.inputs: 388 for inp in block.inputs:
379 portElement = doc.createElement("input") 389 portElement = doc.createElement("input")
380 portElement.setAttribute("name", inp.name) 390 portElement.setAttribute("name", inp.name)
381 blockElement.appendChild(portElement) 391 blockElement.appendChild(portElement)
382 for outp in block.outputs: 392 for outp in block.outputs:
383 portElement = doc.createElement("output") 393 portElement = doc.createElement("output")
384 portElement.setAttribute("name", outp.name) 394 portElement.setAttribute("name", outp.name)
385 blockElement.appendChild(portElement) 395 blockElement.appendChild(portElement)
386 modelElement.appendChild(blockElement) 396 modelElement.appendChild(blockElement)
397 for connection in connections:
398 print(connection)
399 connectionElement = doc.createElement("connection")
400 fromPort = connection.fromPort.name
401 toPort = connection.toPort.name
402 fromBlock = ""
403 toBlock = ""
404 connectionElement.setAttribute("fromBlock", fromBlock)
405 connectionElement.setAttribute("fromPort", fromPort)
406 connectionElement.setAttribute("toBlock", toBlock)
407 connectionElement.setAttribute("toPort", toPort)
408 modelElement.appendChild(connectionElement)
387 with open(filename, 'w') as f: 409 with open(filename, 'w') as f:
388 f.write(doc.toprettyxml()) 410 f.write(doc.toprettyxml())
389 411
390 def loadDiagram(self, filename): 412 def loadDiagram(self, filename):
391 try: 413 try:
406 block = BlockItem(name) 428 block = BlockItem(name)
407 self.diagramScene.addItem(block) 429 self.diagramScene.addItem(block)
408 block.setPos(x, y) 430 block.setPos(x, y)
409 block.sizer.setPos(w, h) 431 block.sizer.setPos(w, h)
410 # Load ports: 432 # Load ports:
411 portElements = blockElement.getElementsByTagName('port') 433 portElements = blockElement.getElementsByTagName('input')
412 for portElement in portElements: 434 for portElement in portElements:
413 print(portElement) 435 name = portElement.getAttribute('name')
436 inp = PortItem(name, block)
437 block.addInput(inp)
438 portElements = blockElement.getElementsByTagName('output')
439 for portElement in portElements:
440 name = portElement.getAttribute('name')
441 outp = PortItem(name, block)
442 block.addOutput(outp)
414 connectionElements = sysElement.getElementsByTagName('connection') 443 connectionElements = sysElement.getElementsByTagName('connection')
415 for connectionElement in connectionElements: 444 for connectionElement in connectionElements:
416 print(connectionElement) 445 print(connectionElement)
417 self.zoomAll() 446 self.zoomAll()
418 447
421 def startConnection(self, port): 450 def startConnection(self, port):
422 self.startedConnection = Connection(port, None) 451 self.startedConnection = Connection(port, None)
423 def zoomAll(self): 452 def zoomAll(self):
424 """ zoom to fit all items """ 453 """ zoom to fit all items """
425 rect = self.diagramScene.itemsBoundingRect() 454 rect = self.diagramScene.itemsBoundingRect()
426 print(rect)
427 self.diagramView.fitInView(rect, Qt.KeepAspectRatio) 455 self.diagramView.fitInView(rect, Qt.KeepAspectRatio)
428 def sceneMouseMoveEvent(self, event): 456 def sceneMouseMoveEvent(self, event):
429 if self.startedConnection: 457 if self.startedConnection:
430 pos = event.scenePos() 458 pos = event.scenePos()
431 self.startedConnection.setEndPos(pos) 459 self.startedConnection.setEndPos(pos)