comparison python/apps/diagrameditor.py @ 63:32078200cdd6

Several move action
author windel
date Sun, 07 Oct 2012 17:04:10 +0200
parents python/lab/diagrameditor.py@fd7d5069734e
children b01311fb3be7
comparison
equal deleted inserted replaced
62:fd7d5069734e 63:32078200cdd6
1 #!/usr/bin/python
2
3 from PyQt4.QtGui import *
4 from PyQt4.QtCore import *
5 import sys
6 import xml.dom.minidom as md
7 import xml
8
9 """
10 Author: Windel Bouwman
11 Year: 2012
12 Description: This script implements a diagram editor.
13 run with python 3.x as:
14 $ python [thisfile.py]
15 """
16
17 class ArrowHead(QGraphicsPathItem):
18 def __init__(self, parent):
19 super(ArrowHead, self).__init__(parent)
20 arrowPath = QPainterPath(QPointF(0.0, 0.0))
21 arrowPath.lineTo(-6.0, 10.0)
22 arrowPath.lineTo(6.0, 10.0)
23 arrowPath.lineTo(0.0, 0.0)
24 self.setPath(arrowPath)
25 pen = QPen(Qt.blue, 2)
26 self.setPen(pen)
27 self.setBrush(QBrush(pen.color()))
28 self.myshape = QPainterPath()
29
30 class Connection(QGraphicsPathItem):
31 """ Implementation of a connection between blocks """
32 def __init__(self, fromPort, toPort):
33 super(Connection, self).__init__()
34 self.pos1 = None
35 self.pos2 = None
36 self.fromPort = None
37 self.toPort = None
38 self.setFlag(self.ItemIsSelectable, True)
39 self.setFlag(self.ItemClipsToShape, True)
40 self.pen = QPen(Qt.blue, 2)
41 self.pen.setCapStyle(Qt.RoundCap)
42 self.setPen(self.pen)
43 self.arrowhead = ArrowHead(self)
44 self.vias = []
45 self.setFromPort(fromPort)
46 self.setToPort(toPort)
47 def mouseDoubleClickEvent(self, event):
48 pos = event.scenePos()
49 pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
50 idx = 0
51 tidx = 0
52 for p1, p2 in zip(pts[0:-1], pts[1:]):
53 l1 = QLineF(p1, p2)
54 l2 = QLineF(p1, pos)
55 l3 = QLineF(pos, p2)
56 d = l2.length() + l3.length() - l1.length()
57 if d < 5:
58 tidx = idx
59 idx += 1
60 self.addHandle(pos, tidx)
61
62 def addHandle(self, pos, idx=None):
63 hi = HandleItem(self)
64 if idx:
65 self.vias.insert(idx, hi)
66 else:
67 self.vias.append(hi)
68 def callback(p):
69 self.updateLineStukken()
70 return p
71 hi.posChangeCallbacks.append(callback)
72 hi.setPos(pos)
73 self.updateLineStukken()
74
75 def setFromPort(self, fromPort):
76 if self.fromPort:
77 self.fromPort.posCallbacks.remove(self.setBeginPos)
78 self.fromPort.connection = None
79 self.fromPort = fromPort
80 if self.fromPort:
81 self.fromPort.connection = self
82 self.setBeginPos(fromPort.scenePos())
83 self.fromPort.posCallbacks.append(self.setBeginPos)
84 def setToPort(self, toPort):
85 if self.toPort:
86 self.toPort.posCallbacks.remove(self.setEndPos)
87 self.toPort.connection = None
88 self.toPort = toPort
89 if self.toPort:
90 self.setEndPos(toPort.scenePos())
91 self.toPort.connection = self
92 self.toPort.posCallbacks.append(self.setEndPos)
93 def releasePorts(self):
94 self.setFromPort(None)
95 self.setToPort(None)
96 def setBeginPos(self, pos1):
97 self.pos1 = pos1
98 self.updateLineStukken()
99 def setEndPos(self, endpos):
100 self.pos2 = endpos
101 self.updateLineStukken()
102 def itemChange(self, change, value):
103 if change == self.ItemSelectedHasChanged:
104 for via in self.vias:
105 via.setVisible(value)
106 return super(Connection, self).itemChange(change, value)
107 def shape(self):
108 return self.myshape
109 def updateLineStukken(self):
110 """
111 This algorithm determines the optimal routing of all signals.
112 TODO: implement nice automatic line router
113 """
114 if self.pos1 is None or self.pos2 is None:
115 return
116 pts = [self.pos1] + [v.pos() for v in self.vias] + [self.pos2]
117 self.arrowhead.setPos(self.pos2)
118 if pts[-1].x() < pts[-2].x():
119 self.arrowhead.setRotation(-90)
120 else:
121 self.arrowhead.setRotation(90)
122 path = QPainterPath(pts[0])
123 for pt in pts[1:]:
124 path.lineTo(pt)
125 self.setPath(path)
126 """ Create a shape outline using the path stroker """
127 s = super(Connection, self).shape()
128 pps = QPainterPathStroker()
129 pps.setWidth(10)
130 self.myshape = pps.createStroke(s).simplified()
131
132 class ParameterDialog(QDialog):
133 def __init__(self, block, parent = None):
134 super(ParameterDialog, self).__init__(parent)
135 self.block = block
136 self.button = QPushButton('Ok', self)
137 l = QGridLayout(self)
138 l.addWidget(QLabel('Name:', self), 0, 0)
139 self.nameEdit = QLineEdit(self.block.name)
140 l.addWidget(self.nameEdit, 0, 1)
141 l.addWidget(QLabel('Code:', self), 1, 0)
142 self.codeEdit = QTextEdit(self)
143 self.codeEdit.setPlainText(self.block.code)
144 l.addWidget(self.codeEdit, 1, 1)
145 l.addWidget(self.button, 2, 0, 1, 2)
146 self.button.clicked.connect(self.OK)
147 def OK(self):
148 self.block.setName(self.nameEdit.text())
149 self.block.code = self.codeEdit.toPlainText()
150 self.close()
151
152 class PortItem(QGraphicsPathItem):
153 """ Represents a port to a subsystem """
154 def __init__(self, name, block, direction):
155 super(PortItem, self).__init__(block)
156 self.connection = None
157 path = QPainterPath()
158 d = 10.0
159 if direction == 'input':
160 path.moveTo(-d, -d)
161 path.lineTo(0.0, 0.0)
162 path.lineTo(-d, d)
163 else:
164 path.moveTo(0.0, -d)
165 path.lineTo(d, 0.0)
166 path.lineTo(0.0, d)
167 self.setPath(path)
168 self.direction = direction
169 self.block = block
170 self.setCursor(QCursor(Qt.CrossCursor))
171 pen = QPen(Qt.blue, 2)
172 pen.setCapStyle(Qt.RoundCap)
173 self.setPen(pen)
174 self.name = name
175 self.textItem = QGraphicsTextItem(name, self)
176 self.setName(name)
177 self.posCallbacks = []
178 self.setFlag(self.ItemSendsScenePositionChanges, True)
179 def setName(self, name):
180 self.name = name
181 self.textItem.setPlainText(name)
182 rect = self.textItem.boundingRect()
183 lw, lh = rect.width(), rect.height()
184 if self.direction == 'input':
185 lx = 3
186 else:
187 lx = -3 - lw
188 self.textItem.setPos(lx, -lh / 2)
189 def itemChange(self, change, value):
190 if change == self.ItemScenePositionHasChanged:
191 for cb in self.posCallbacks:
192 cb(value)
193 return value
194 return super(PortItem, self).itemChange(change, value)
195 def mousePressEvent(self, event):
196 if self.direction == 'output':
197 self.scene().startConnection(self)
198
199 class OutputPort(PortItem):
200 # TODO: create a subclass OR make a member porttype
201 pass
202
203 # Block part:
204 class HandleItem(QGraphicsEllipseItem):
205 """ A handle that can be moved by the mouse """
206 def __init__(self, parent=None):
207 dx = 13.0
208 super(HandleItem, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent)
209 self.posChangeCallbacks = []
210 self.setBrush(QBrush(Qt.white))
211 self.setFlag(self.ItemSendsScenePositionChanges, True)
212 self.setFlag(self.ItemIsMovable, True)
213 self.setVisible(False)
214 self.setCursor(QCursor(Qt.SizeFDiagCursor))
215 def mouseMoveEvent(self, event):
216 """ Move function without moving the other selected elements """
217 p = self.mapToParent(event.pos())
218 self.setPos(p)
219 def mySetPos(self, p):
220 # TODO: use this instead of itemChange?
221 self.setPos(p)
222 def itemChange(self, change, value):
223 if change == self.ItemPositionChange:
224 for cb in self.posChangeCallbacks:
225 res = cb(value)
226 if res:
227 value = res
228 return value
229 # Call superclass method:
230 return super(HandleItem, self).itemChange(change, value)
231
232 def uniqify(name, names):
233 newname = name
234 i = 1
235 while newname in names:
236 newname = name + str(i)
237 i += 1
238 return newname
239
240 class BlockItem(QGraphicsRectItem):
241 """
242 Represents a block in the diagram
243 Has an x and y and width and height
244 width and height can only be adjusted with a tip in the lower right corner.
245
246 - in and output ports
247 - parameters
248 - description
249 """
250 def __init__(self, name='Untitled', parent=None):
251 super(BlockItem, self).__init__(parent)
252 # Properties of the rectangle:
253 self.setPen(QPen(Qt.blue, 2))
254 self.setBrush(QBrush(Qt.lightGray))
255 self.setFlags(self.ItemIsSelectable | self.ItemIsMovable)
256 self.setFlag(self.ItemSendsScenePositionChanges, True)
257 self.setCursor(QCursor(Qt.PointingHandCursor))
258 self.label = QGraphicsTextItem(name, self)
259 self.name = name
260 self.code = ''
261 # Create corner for resize:
262 self.sizer = HandleItem(self)
263 self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback
264 button = QPushButton('+in')
265 button.clicked.connect(self.newInputPort)
266 self.buttonItemAddInput = QGraphicsProxyWidget(self)
267 self.buttonItemAddInput.setWidget(button)
268 self.buttonItemAddInput.setVisible(False)
269 button = QPushButton('+out')
270 button.clicked.connect(self.newOutputPort)
271 self.buttonItemAddOutput = QGraphicsProxyWidget(self)
272 self.buttonItemAddOutput.setWidget(button)
273 self.buttonItemAddOutput.setVisible(False)
274
275 # Inputs and outputs of the block:
276 self.inputs = []
277 self.outputs = []
278 # Update size:
279 self.sizer.mySetPos(QPointF(60, 40)) # This is a better resize function
280 def editParameters(self):
281 pd = ParameterDialog(self, self.window())
282 pd.exec_()
283 def mouseDoubleClickEvent(self, event):
284 self.editParameters()
285 def newInputPort(self):
286 names = [i.name for i in self.inputs + self.outputs]
287 self.addInput(PortItem(uniqify('in', names), self, 'input'))
288 def newOutputPort(self):
289 names = [i.name for i in self.inputs + self.outputs]
290 self.addOutput(PortItem(uniqify('out', names), self, 'output'))
291 def setName(self, name):
292 self.name = name
293 self.label.setPlainText(name)
294 def addInput(self, i):
295 self.inputs.append(i)
296 self.updateSize()
297 def addOutput(self, o):
298 self.outputs.append(o)
299 self.updateSize()
300
301 def contextMenuEvent(self, event):
302 menu = QMenu()
303 pa = menu.addAction('Parameters')
304 pa.triggered.connect(self.editParameters)
305 menu.exec_(event.screenPos())
306 def itemChange(self, change, value):
307 if change == self.ItemSelectedHasChanged:
308 self.sizer.setVisible(value)
309 self.buttonItemAddInput.setVisible(value)
310 self.buttonItemAddOutput.setVisible(value)
311 return super(BlockItem, self).itemChange(change, value)
312
313 def updateSize(self):
314 rect = self.rect()
315 h, w = rect.height(), rect.width()
316 self.buttonItemAddInput.setPos(0, h + 4)
317 self.buttonItemAddOutput.setPos(w+10, h+4)
318 if len(self.inputs) == 1:
319 self.inputs[0].setPos(0.0, h / 2)
320 elif len(self.inputs) > 1:
321 y = 15
322 dy = (h - 30) / (len(self.inputs) - 1)
323 for inp in self.inputs:
324 inp.setPos(0.0, y)
325 y += dy
326 if len(self.outputs) == 1:
327 self.outputs[0].setPos(w, h / 2)
328 elif len(self.outputs) > 1:
329 y = 15
330 dy = (h - 30) / (len(self.outputs) - 1)
331 for outp in self.outputs:
332 outp.setPos(w, y)
333 y += dy
334
335 def changeSize(self, p):
336 """ Resize block function """
337 w, h = p.x(), p.y()
338 # Limit the block size:
339 if h < 20:
340 h = 20
341 if w < 40:
342 w = 40
343 self.setRect(0.0, 0.0, w, h)
344 # center label:
345 rect = self.label.boundingRect()
346 lw, lh = rect.width(), rect.height()
347 lx = (w - lw) / 2
348 ly = (h - lh) / 2
349 self.label.setPos(lx, ly)
350 # Update port positions:
351 self.updateSize()
352 return QPointF(w, h)
353
354 class EditorGraphicsView(QGraphicsView):
355 def __init__(self, scene, parent=None):
356 QGraphicsView.__init__(self, scene, parent)
357 self.setDragMode(QGraphicsView.RubberBandDrag)
358 def wheelEvent(self, event):
359 pos = event.pos()
360 posbefore = self.mapToScene(pos)
361 degrees = event.delta() / 8.0
362 sx = (100.0 + degrees) / 100.0
363 self.scale(sx, sx)
364 event.accept()
365 def dragEnterEvent(self, event):
366 if event.mimeData().hasFormat('component/name'):
367 event.accept()
368 def dragMoveEvent(self, event):
369 if event.mimeData().hasFormat('component/name'):
370 event.accept()
371 def dropEvent(self, event):
372 if event.mimeData().hasFormat('component/name'):
373 name = bytes(event.mimeData().data('component/name')).decode()
374 pos = self.mapToScene(event.pos())
375 self.scene().addNewBlock(pos, name)
376
377 class LibraryModel(QStandardItemModel):
378 def __init__(self, parent=None):
379 QStandardItemModel.__init__(self, parent)
380 def mimeTypes(self):
381 return ['component/name']
382 def mimeData(self, idxs):
383 mimedata = QMimeData()
384 for idx in idxs:
385 if idx.isValid():
386 txt = self.data(idx, Qt.DisplayRole) # python 3
387 mimedata.setData('component/name', txt)
388 return mimedata
389
390 class DiagramScene(QGraphicsScene):
391 """ Save and load and deletion of item"""
392 def __init__(self, parent=None):
393 super(DiagramScene, self).__init__(parent)
394 self.startedConnection = None
395
396 def saveDiagram(self, filename):
397 items = self.items()
398 blocks = [item for item in items if type(item) is BlockItem]
399 connections = [item for item in items if type(item) is Connection]
400
401 doc = md.Document()
402 modelElement = doc.createElement('system')
403 doc.appendChild(modelElement)
404 for block in blocks:
405 blockElement = doc.createElement("block")
406 x, y = block.scenePos().x(), block.scenePos().y()
407 rect = block.rect()
408 w, h = rect.width(), rect.height()
409 blockElement.setAttribute("name", block.name)
410 blockElement.setAttribute("x", str(int(x)))
411 blockElement.setAttribute("y", str(int(y)))
412 blockElement.setAttribute("width", str(int(w)))
413 blockElement.setAttribute("height", str(int(h)))
414 codeNode = doc.createCDATASection(block.code)
415 codeElement = doc.createElement('code')
416 codeElement.appendChild(codeNode)
417 blockElement.appendChild(codeElement)
418 for inp in block.inputs:
419 portElement = doc.createElement("input")
420 portElement.setAttribute("name", inp.name)
421 blockElement.appendChild(portElement)
422 for outp in block.outputs:
423 portElement = doc.createElement("output")
424 portElement.setAttribute("name", outp.name)
425 blockElement.appendChild(portElement)
426 modelElement.appendChild(blockElement)
427 for connection in connections:
428 connectionElement = doc.createElement("connection")
429 fromPort = connection.fromPort.name
430 toPort = connection.toPort.name
431 fromBlock = connection.fromPort.block.name
432 toBlock = connection.toPort.block.name
433 connectionElement.setAttribute("fromBlock", fromBlock)
434 connectionElement.setAttribute("fromPort", fromPort)
435 connectionElement.setAttribute("toBlock", toBlock)
436 connectionElement.setAttribute("toPort", toPort)
437 for via in connection.vias:
438 viaElement = doc.createElement('via')
439 viaElement.setAttribute('x', str(int(via.x())))
440 viaElement.setAttribute('y', str(int(via.y())))
441 connectionElement.appendChild(viaElement)
442 modelElement.appendChild(connectionElement)
443 with open(filename, 'w') as f:
444 f.write(doc.toprettyxml())
445
446 def loadDiagram(self, filename):
447 try:
448 doc = md.parse(filename)
449 except IOError as e:
450 print('{0} not found'.format(filename))
451 return
452 except xml.parsers.expat.ExpatError as e:
453 print('{0}'.format(e))
454 return
455 sysElements = doc.getElementsByTagName('system')
456 blockElements = doc.getElementsByTagName('block')
457 for sysElement in sysElements:
458 blockElements = sysElement.getElementsByTagName('block')
459 for blockElement in blockElements:
460 x = float(blockElement.getAttribute('x'))
461 y = float(blockElement.getAttribute('y'))
462 w = float(blockElement.getAttribute('width'))
463 h = float(blockElement.getAttribute('height'))
464 name = blockElement.getAttribute('name')
465 block = BlockItem(name)
466 self.addItem(block)
467 block.setPos(x, y)
468 block.sizer.setPos(w, h)
469 codeElements = blockElement.getElementsByTagName('code')
470 if codeElements:
471 cn = codeElements[0].childNodes
472 cdatas = [cd for cd in cn if type(cd) is md.CDATASection]
473 if len(cdatas) > 0:
474 block.code = cdatas[0].data
475 # Load ports:
476 portElements = blockElement.getElementsByTagName('input')
477 for portElement in portElements:
478 name = portElement.getAttribute('name')
479 inp = PortItem(name, block, 'input')
480 block.addInput(inp)
481 portElements = blockElement.getElementsByTagName('output')
482 for portElement in portElements:
483 name = portElement.getAttribute('name')
484 outp = PortItem(name, block, 'output')
485 block.addOutput(outp)
486 connectionElements = sysElement.getElementsByTagName('connection')
487 for connectionElement in connectionElements:
488 fromBlock = connectionElement.getAttribute('fromBlock')
489 fromPort = connectionElement.getAttribute('fromPort')
490 toBlock = connectionElement.getAttribute('toBlock')
491 toPort = connectionElement.getAttribute('toPort')
492 viaElements = connectionElement.getElementsByTagName('via')
493 fromPort = self.findPort(fromBlock, fromPort)
494 toPort = self.findPort(toBlock, toPort)
495 connection = Connection(fromPort, toPort)
496 for viaElement in viaElements:
497 x = int(viaElement.getAttribute('x'))
498 y = int(viaElement.getAttribute('y'))
499 connection.addHandle(QPointF(x, y))
500 self.addItem(connection)
501 def findPort(self, blockname, portname):
502 items = self.items()
503 blocks = [item for item in items if type(item) is BlockItem]
504 for block in [b for b in blocks if b.name == blockname]:
505 for port in block.inputs + block.outputs:
506 if port.name == portname:
507 return port
508 def addNewBlock(self, pos, name):
509 blocknames = [item.name for item in self.items() if type(item) is BlockItem]
510 b1 = BlockItem(uniqify(name, blocknames))
511 b1.setPos(pos)
512 self.addItem(b1)
513 def mouseMoveEvent(self, event):
514 if self.startedConnection:
515 pos = event.scenePos()
516 self.startedConnection.setEndPos(pos)
517 super(DiagramScene, self).mouseMoveEvent(event)
518 def mouseReleaseEvent(self, event):
519 if self.startedConnection:
520 items = self.items(event.scenePos())
521 for item in items:
522 if type(item) is PortItem:
523 self.startedConnection.setToPort(item)
524 self.startedConnection = None
525 return
526 self.deleteItem(self.startedConnection)
527 self.startedConnection = None
528 super(DiagramScene, self).mouseReleaseEvent(event)
529 def startConnection(self, port):
530 self.startedConnection = Connection(port, None)
531 pos = port.scenePos()
532 self.startedConnection.setEndPos(pos)
533 self.addItem(self.startedConnection)
534 def deleteItem(self, item=None):
535 if item:
536 if type(item) is BlockItem:
537 for p in item.inputs + item.outputs:
538 if p.connection:
539 self.deleteItem(p.connection)
540 self.removeItem(item)
541 elif type(item) is Connection:
542 item.releasePorts()
543 self.removeItem(item)
544 else:
545 # No item was supplied, try to delete all currently selected items:
546 items = self.selectedItems()
547 connections = [item for item in items if type(item) is Connection]
548 blocks = [item for item in items if type(item) is BlockItem]
549 for item in connections + blocks:
550 self.deleteItem(item)
551
552 class DiagramEditor(QWidget):
553 def __init__(self, parent=None):
554 QWidget.__init__(self, parent)
555
556 # Widget layout and child widgets:
557 self.horizontalLayout = QHBoxLayout(self)
558 self.diagramScene = DiagramScene(self)
559 self.loadDiagram = self.diagramScene.loadDiagram
560 self.diagramView = EditorGraphicsView(self.diagramScene, self)
561 self.horizontalLayout.addWidget(self.diagramView)
562
563 testShortcut = QShortcut(QKeySequence("F12"), self)
564 testShortcut.activated.connect(self.test)
565 delShort = QShortcut(QKeySequence.Delete, self)
566 delShort.activated.connect(self.diagramScene.deleteItem)
567
568 def test(self):
569 self.diagramView.rotate(30)
570 self.zoomAll()
571 def save(self):
572 self.diagramScene.saveDiagram('diagram2.usd')
573 def load(self):
574 filename = QFileDialog.getOpenFileName(self)
575 self.diagramScene.loadDiagram(filename)
576 def zoomAll(self):
577 """ zoom to fit all items """
578 rect = self.diagramScene.itemsBoundingRect()
579 self.diagramView.fitInView(rect, Qt.KeepAspectRatio)
580
581 class LibraryWidget(QListView):
582 def __init__(self):
583 super(LibraryWidget, self).__init__(None)
584 self.libraryModel = LibraryModel(self)
585 self.libraryModel.setColumnCount(1)
586 # Create an icon with an icon:
587 pixmap = QPixmap(60, 60)
588 pixmap.fill()
589 painter = QPainter(pixmap)
590 painter.fillRect(10, 10, 40, 40, Qt.blue)
591 painter.setBrush(Qt.yellow)
592 painter.drawEllipse(20, 20, 20, 20)
593 painter.end()
594
595 # Fill library:
596 self.libItems = []
597 self.libItems.append( QStandardItem(QIcon(pixmap), 'Block') )
598 self.libItems.append( QStandardItem(QIcon(pixmap), 'Uber Unit') )
599 self.libItems.append( QStandardItem(QIcon(pixmap), 'Device') )
600 for i in self.libItems:
601 self.libraryModel.appendRow(i)
602 self.setModel(self.libraryModel)
603 self.setViewMode(self.IconMode)
604 self.setDragDropMode(self.DragOnly)
605
606 class Main(QMainWindow):
607 def __init__(self):
608 super(Main, self).__init__(None)
609 self.editor = DiagramEditor()
610 self.setCentralWidget(self.editor)
611 self.setWindowTitle("Diagram editor")
612 toolbar = self.addToolBar('Tools')
613
614 saveAction = QAction('Save', self)
615 saveAction.setShortcuts(QKeySequence.Save)
616 saveAction.triggered.connect(self.editor.save)
617 toolbar.addAction(saveAction)
618 openAction = QAction('Open', self)
619 openAction.setShortcuts(QKeySequence.Open)
620 openAction.triggered.connect(self.editor.load)
621 toolbar.addAction(openAction)
622 fullScreenAction = QAction('Full screen', self)
623 fullScreenAction.setShortcuts(QKeySequence("F11"))
624 fullScreenAction.triggered.connect(self.toggleFullScreen)
625 toolbar.addAction(fullScreenAction)
626 zoomAction = QAction('Fit in view', self)
627 zoomAction.setShortcuts(QKeySequence('F8'))
628 zoomAction.triggered.connect(self.editor.zoomAll)
629 toolbar.addAction(zoomAction)
630
631 self.library = LibraryWidget()
632 libraryDock = QDockWidget('Library', self)
633 libraryDock.setWidget(self.library)
634 self.addDockWidget(Qt.LeftDockWidgetArea, libraryDock)
635
636 self.editor.loadDiagram('diagram2.usd')
637
638 def toggleFullScreen(self):
639 self.setWindowState(self.windowState() ^ Qt.WindowFullScreen)
640 self.editor.zoomAll()
641
642 if __name__ == '__main__':
643 if sys.version_info.major != 3:
644 print('Please use python 3.x')
645 sys.exit(1)
646
647 app = QApplication(sys.argv)
648 main = Main()
649 main.show()
650 main.resize(700, 500)
651 main.editor.zoomAll()
652 app.exec_()
653