Mercurial > lcfOS
changeset 55:9414bad225a8
Added nice resizing
author | windel |
---|---|
date | Thu, 19 Apr 2012 18:16:34 +0200 |
parents | d8163d2c3779 |
children | 7a82f0d52e85 |
files | applications/lab/diagrameditor.py |
diffstat | 1 files changed, 142 insertions(+), 79 deletions(-) [+] |
line wrap: on
line diff
--- a/applications/lab/diagrameditor.py Thu Apr 12 18:03:33 2012 +0200 +++ b/applications/lab/diagrameditor.py Thu Apr 19 18:16:34 2012 +0200 @@ -13,6 +13,28 @@ $ python [thisfile.py] """ +class ArrowHead(QGraphicsPathItem): + def __init__(self, parent): + super(ArrowHead, self).__init__(parent) + arrowPath = QPainterPath(QPointF(0.0, 0.0)) + arrowPath.lineTo(-6.0, 10.0) + arrowPath.lineTo(6.0, 10.0) + arrowPath.lineTo(0.0, 0.0) + self.setPath(arrowPath) + pen = QPen(Qt.blue, 2) + self.setPen(pen) + self.setBrush(QBrush(pen.color())) + +class LinePart(QGraphicsLineItem): + def __init__(self, x1, y1, x2, y2): + super(LinePart, self).__init__(x1, y1, x2, y2) + self.setFlags(self.ItemIsSelectable) + def boundingRect(self): + rect = super(LinePart, self).boundingRect() + rect.adjust(-2, -2, 2, 2) + return rect + +#class Connection(QGraphicsItemGroup): class Connection(QGraphicsPathItem): """ Implementation of a connection between blocks """ def __init__(self, fromPort, toPort): @@ -22,21 +44,19 @@ self.fromPort = None self.toPort = None #self.setFlags(self.ItemIsSelectable | self.ItemIsMovable) - self.setFlags(self.ItemIsSelectable) - pen = QPen(Qt.blue, 2) - pen.setCapStyle(Qt.RoundCap) - self.setPen(pen) - arrowPath = QPainterPath(QPointF(0.0, 0.0)) - arrowPath.lineTo(-6.0, 10.0) - arrowPath.lineTo(6.0, 10.0) - arrowPath.lineTo(0.0, 0.0) - self.arrowhead = QGraphicsPathItem(arrowPath, self) - self.arrowhead.setPen(pen) - self.arrowhead.setBrush(QBrush(pen.color())) + self.setFlag(self.ItemIsSelectable, True) + self.setFlag(self.ItemClipsToShape, True) + self.pen = QPen(Qt.blue, 2) + self.pen.setCapStyle(Qt.RoundCap) + self.setPen(self.pen) + self.arrowhead = ArrowHead(self) + #self.addToGroup(self.arrowhead) + #self.body = QGraphicsPathItem(self) + #self.body.setPen(self.pen) + #self.addToGroup(self.body) + self.lineItems = [] self.setFromPort(fromPort) self.setToPort(toPort) - #def shape(self): - # return self.path() def setFromPort(self, fromPort): if self.fromPort: self.fromPort.posCallbacks.remove(self.setBeginPos) @@ -64,6 +84,10 @@ def setEndPos(self, endpos): self.pos2 = endpos self.updateLineStukken() + def itemChange(self, change, value): + if change == self.ItemSelectedHasChanged: + pass # TODO, do something useful here. + return super(Connection, self).itemChange(change, value) def updateLineStukken(self): """ This algorithm determines the optimal routing of all signals. @@ -71,69 +95,44 @@ """ if self.pos1 is None or self.pos2 is None: return + # TODO: do not get the scene here? - ds = editor.diagramScene - self.arrowhead.setPos(self.pos2) - # TODO: create pieces of lines. # Determine the begin and end positions: - p1 = self.pos1 - p4 = self.pos2 - x1, y1 = self.pos1.x(), self.pos1.y() - x4, y4 = self.pos2.x(), self.pos2.y() + pts = editor.diagramScene.vias(self.pos1, self.pos2) - def stripHits(hits): - """ Helper function that removes object hits """ - hits = [hit for hit in hits if type(hit) is BlockItem] - if self in hits: - hits.remove(self) - if self.fromPort in hits: - hits.remove(self.fromPort) - if self.toPort in hits: - hits.remove(self.toPort) - return hits - - def viaPoints(pA, pB): - # Construct intermediate points: + self.arrowhead.setPos(self.pos2) + if pts[-1].x() < pts[-2].x(): + self.arrowhead.setRotation(-90) + else: + self.arrowhead.setRotation(90) - pAB1 = QPointF(pA.x(), pB.y()) - pAB2 = QPointF(pB.x(), pA.y()) - path1 = QPainterPath(pA) - path1.lineTo(pAB1) - path2 = QPainterPath(pA) - path2.lineTo(pAB2) - if len(stripHits(ds.items(path1))) > len(stripHits(ds.items(path2))): - pAB = pAB2 - else: - pAB = pAB1 - return [pAB] - - # Determine left or right: - dx = QPointF(20, 0) - if len(stripHits(ds.items(p1 + dx))) > len(stripHits(ds.items(p1 - dx))): - p2 = p1 - dx - else: - p2 = p1 + dx + usePath = True + if usePath: + path = QPainterPath(pts[0]) + # Now move from p2 to p3 without hitting blocks: + for pt in pts[1:]: + path.lineTo(pt) + for pt in reversed(pts[0:-1]): + path.lineTo(pt) + path.closeSubpath() - if len(stripHits(ds.items(p4 + dx))) > len(stripHits(ds.items(p4 - dx))): - p3 = p4 - dx - self.arrowhead.setRotation(90) + #self.body.setPath(path) + self.setPath(path) + #outline = QGraphicsPathItem(self.shape()) + #editor.diagramScene.addItem(outline) else: - p3 = p4 + dx - self.arrowhead.setRotation(-90) - - path = QPainterPath(p1) - path.lineTo(p2) - # Now move from p2 to p3 without hitting blocks: - pts = viaPoints(p2, p3) - for pt in pts: - path.lineTo(pt) - path.lineTo(p3) - path.lineTo(p4) - - hits = stripHits(ds.items(path)) - self.setPath(path) + # Delete old line items: + for li in self.lineItems: + editor.diagramScene.removeItem(li) + #self.removeFromGroup(li) + self.lineItems = [] + for a, b in zip(pts[0:-1], pts[1:]): + li = LinePart(a.x(),a.y(),b.x(),b.y()) + #self.addToGroup(li) + editor.diagramScene.addItem(li) + self.lineItems.append(li) class ParameterDialog(QDialog): def __init__(self, block, parent = None): @@ -196,6 +195,11 @@ self.setName(name) self.posCallbacks = [] self.setFlag(self.ItemSendsScenePositionChanges, True) + def boundingRect(self): + rect = super(PortItem, self).boundingRect() + dx = 8 + rect.adjust(-dx, -dx, dx, dx) + return rect def setName(self, name): self.name = name self.textItem.setPlainText(name) @@ -224,22 +228,23 @@ class HandleItem(QGraphicsEllipseItem): """ A handle that can be moved by the mouse """ def __init__(self, parent=None): - super(HandleItem, self).__init__(QRectF(-4.0,-4.0,8.0,8.0), parent) + dx = 12.0 + super(HandleItem, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) self.posChangeCallbacks = [] self.setBrush(QBrush(Qt.white)) self.setFlag(self.ItemSendsScenePositionChanges, True) self.setFlag(self.ItemIsMovable, True) - #self.setFlag(self.ItemIsSelectable, False) self.setCursor(QCursor(Qt.SizeFDiagCursor)) - + def mouseMoveEvent(self, event): + """ Move function without moving the other selected elements """ + p = self.mapToParent(event.pos()) + self.setPos(p) def itemChange(self, change, value): if change == self.ItemPositionChange: - x, y = value.x(), value.y() for cb in self.posChangeCallbacks: - res = cb(x, y) + res = cb(value) if res: - x, y = res - value = QPointF(x, y) + value = res return value # Call superclass method: return super(HandleItem, self).itemChange(change, value) @@ -267,7 +272,7 @@ # Create corner for resize: self.sizer = HandleItem(self) self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback - #self.sizer.setVisible(False) + self.sizer.setVisible(False) # Inputs and outputs of the block: self.inputs = [] @@ -290,6 +295,10 @@ def addOutput(self, o): self.outputs.append(o) self.updateSize() + def boundingRect(self): + rect = super(BlockItem, self).boundingRect() + rect.adjust(-3, -3, 3, 3) + return rect def contextMenuEvent(self, event): menu = QMenu() @@ -298,6 +307,10 @@ pa = menu.addAction('Add port') pa.triggered.connect(self.addPort) menu.exec_(event.screenPos()) + def itemChange(self, change, value): + if change == self.ItemSelectedHasChanged: + self.sizer.setVisible(value) + return super(BlockItem, self).itemChange(change, value) def updateSize(self): rect = self.rect() @@ -319,8 +332,9 @@ outp.setPos(w, y) y += dy - def changeSize(self, w, h): + def changeSize(self, p): """ Resize block function """ + w, h = p.x(), p.y() # Limit the block size: if h < 20: h = 20 @@ -335,12 +349,19 @@ self.label.setPos(lx, ly) # Update port positions: self.updateSize() - return w, h + return QPointF(w, h) class EditorGraphicsView(QGraphicsView): def __init__(self, scene, parent=None): QGraphicsView.__init__(self, scene, parent) self.setDragMode(QGraphicsView.RubberBandDrag) + def wheelEvent(self, event): + pos = event.pos() + posbefore = self.mapToScene(pos) + degrees = event.delta() / 8.0 + sx = (100.0 + degrees) / 100.0 + self.scale(sx, sx) + event.accept() def dragEnterEvent(self, event): if event.mimeData().hasFormat('component/name'): event.accept() @@ -371,6 +392,9 @@ """ Save and load and deletion of item""" def __init__(self, parent=None): super(DiagramScene, self).__init__(parent) + #circle = QGraphicsEllipseItem(-10,-10,20,20) + #self.addItem(circle) + def saveDiagram(self, filename): items = self.items() blocks = [item for item in items if type(item) is BlockItem] @@ -466,6 +490,44 @@ def mouseReleaseEvent(self, mouseEvent): editor.sceneMouseReleaseEvent(mouseEvent) super(DiagramScene, self).mouseReleaseEvent(mouseEvent) + def vias(self, P1, P2): + """ Constructs a list of points that must be connected + to go from P1 to P2 """ + def stripHits(hits): + """ Helper function that removes object hits """ + hits = [hit for hit in hits if type(hit) is BlockItem] + return hits + + def viaPoints(pA, pB): + # Construct intermediate points: + + pAB1 = QPointF(pA.x(), pB.y()) + pAB2 = QPointF(pB.x(), pA.y()) + path1 = QPainterPath(pA) + path1.lineTo(pAB1) + path2 = QPainterPath(pA) + path2.lineTo(pAB2) + if len(stripHits(self.items(path1))) > len(stripHits(self.items(path2))): + pAB = pAB2 + else: + pAB = pAB1 + return [pAB] + + # Determine left or right: + dx = QPointF(20, 0) + if len(stripHits(self.items(P1 + dx))) > len(stripHits(self.items(P1 - dx))): + p2 = P1 - dx + else: + p2 = P1 + dx + + if len(stripHits(self.items(P2 + dx))) > len(stripHits(self.items(P2 - dx))): + p3 = P2 - dx + else: + p3 = P2 + dx + + # If pathitem: + pts = [P1, p2] + viaPoints(p2, p3) + [p3, P2] + return pts def deleteItem(self, item=None): if item: if type(item) is BlockItem: @@ -544,6 +606,8 @@ self.setWindowState(self.windowState() ^ Qt.WindowFullScreen) def startConnection(self, port): self.startedConnection = Connection(port, None) + pos = port.scenePos() + self.startedConnection.setEndPos(pos) self.diagramScene.addItem(self.startedConnection) def zoomAll(self): """ zoom to fit all items """ @@ -563,7 +627,6 @@ self.startedConnection = None return self.diagramScene.deleteItem(self.startedConnection) - del self.startedConnection self.startedConnection = None if __name__ == '__main__':