# HG changeset patch # User windel # Date 1353691649 -3600 # Node ID 367006d423ae0f5d85132b393070e46bc2104942 # Parent 317a73256bf00adb985553d7fe8c993d580dac26 Changed selection handles diff -r 317a73256bf0 -r 367006d423ae python/apps/diagrameditor.py --- a/python/apps/diagrameditor.py Sun Nov 18 14:25:01 2012 +0100 +++ b/python/apps/diagrameditor.py Fri Nov 23 18:27:29 2012 +0100 @@ -17,6 +17,11 @@ newicon = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADSUlEQVR42r2XO0xaURjHLzYmdXRi\nYXBwdDScJl3djQNLw0TSAR88FR9JU6P4QKMCaTqQdGgXsQQMQXFwcGXhCCG+4iNEo6mDMZL45kK/\n76THUO+9Qi/efskJcm4O3+/+v8f51Al/LBaLmTs6Or43gAkq7eHhoXh2dmZva2v7WusZHf9jZGTE\nPz4+blfjuFwuC6IoCsViUbi8vCwDhM1oNH75V4AAANjUAJRKJeYc183NjXB1dVU+Pj7+CIp+qxlg\neHg44PV6VQHg2z8+PjKAu7s7oampSQAVxL29PUtnZ+eP/waA6/r6WtDr9UyJ09NTcXd319LV1aUI\n8QQwNDQUmJiYqBsA5BcMBgPLC4Q4OTkRt7e3LSaTSRbiCWBwcDAwOTmpOgfQOVSBcHFxIbS0tLB9\nDgH5IGYyGYvZbJZAvAoAOsL4IwTEngGxH9fp2MJnhUJBTKfT5t7e3rAsgMfjYQB4oB4V+OJAuI+A\naGtra6nz8/P3o6OjJQnAwMBAYGpqSjUAh8Aw8JLE3OBqIMTGxgbNZrPE5/MVFQFUe6+A4M5xoWMe\nIg4ASksB+vv761aAG3eKMFwBBFhfX6ebm5sEyl0eAHOgjqvgRSBUA3KAghHouFIAt9vNFNACgIci\nmUxSKEcyNjYmBXC5XK8Wgudvz8OAAJgDigD1lGE155UAsiFwOp2vUgVKzisBZJMQAdR2wlqco62s\nrNBcLkfgzpECOByOuhXg5cc733NLJBIMQLYP2O12BqB0uJrjyk8li8fjdGtri4AfeQAMAUpXayJW\ne2MlANlWbLPZGAB2LPagCoQapZaXl+nOzg6ZmZmRB8CBhANoYTB5U5iQyOzsrDwAjmR4hWpl0WiU\nwpxI5ubmpAAwKLAQ4HWqlUUiEbq/v0/m5+flATAEWiqwtLREDw4OyMLCgjyA1iEIh8P08PCQ+P1+\nKUBPTw8D0DIEi4uL9OjoiASDQSlAd3c3A7i/v9cUAKsgFApJAaxWa2B6etp2e3v71yGlen++X60v\n4EwAAOlUKvUO+oEUoLW19UNfX9/nxsbGhsoOxy+Wl75X2+drdXX1J/zX9AkmI+lU3N7e/hYSxAAT\n0Rs+FdX69rXsA1y5ubn5Vz6fL1Q++w30VO4/0/9IewAAAABJRU5ErkJggg==\n' +def enum(**enums): + return type('Enum', (), enums) + +Position = enum(TOP=0, TOP_RIGHT=1, RIGHT=2, BOTTOM_RIGHT=3, BOTTOM=4, BOTTOM_LEFT=5, LEFT=6, TOP_LEFT=7) + def buildPath(pts): path = QPainterPath(pts[0]) for pt in pts[1:]: path.lineTo(pt) @@ -190,11 +195,11 @@ super(InputPort, self).__init__(name, block) self.setPath(buildPath([QPointF(-d, -d), QPointF(0, 0), QPointF(-d, d)])) -class HandleItem(QGraphicsEllipseItem): +class Handle(QGraphicsEllipseItem): """ A handle that can be moved by the mouse """ def __init__(self, parent=None): dx = 13.0 - super(HandleItem, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) + super(Handle, self).__init__(QRectF(-0.5*dx,-0.5*dx,dx,dx), parent) self.posChangeCallbacks = [] self.setBrush(QBrush(Qt.white)) self.setFlags(self.ItemSendsScenePositionChanges | self.ItemIsMovable) @@ -210,7 +215,22 @@ res = cb(value) if res: value = res return value - return super(HandleItem, self).itemChange(change, value) + return super(Handle, self).itemChange(change, value) + +class ResizeSelectionHandle(Handle): + def __init__(self, position): + super(ResizeSelectionHandle, self).__init__() + self.position = position + if self.position in [Position.TOP_LEFT, Position.BOTTOM_RIGHT]: + self.setCursor(QCursor(Qt.SizeFDiagCursor)) + elif self.position in [Position.TOP_RIGHT, Position.BOTTOM_LEFT]: + self.setCursor(QCursor(Qt.SizeBDiagCursor)) + elif self.position in [Position.TOP, Position.BOTTOM]: + self.setCursor(QCursor(Qt.SizeVerCursor)) + elif self.position in [Position.LEFT, Position.RIGHT]: + self.setCursor(QCursor(Qt.SizeHorCursor)) + def mouseMoveEvent(self, event): + self.scene().sizerMoveEvent(self, event.scenePos()) class BlockItem(QGraphicsRectItem): """ Represents a block in the diagram """ @@ -227,8 +247,6 @@ self.name = name self.code = '' # Create corner for resize: - self.sizer = HandleItem(self) - self.sizer.posChangeCallbacks.append(self.changeSize) # Connect the callback button = QPushButton('+in') button.clicked.connect(self.newInputPort) self.buttonItemAddInput = QGraphicsProxyWidget(self) @@ -242,8 +260,6 @@ # Inputs and outputs of the block: self.inputs = [] self.outputs = [] - # Update size: - self.sizer.setPos(QPointF(60, 40)) # This is a better resize function def editParameters(self): pd = ParameterDialog(self, self.window()) pd.exec_() @@ -276,7 +292,7 @@ self.name = d['name'] self.code = d['code'] self.setPos(d['x'], d['y']) - self.sizer.setPos(d['width'], d['height']) + self.changeSize(d['width'], d['height']) for inp in d['inputs']: self.addInput(InputPort(inp['name'], self)) for outp in d['outputs']: self.addOutput(OutputPort(outp['name'], self)) self.subModel.Dict = d['submodel'] @@ -287,7 +303,6 @@ c += indent(self.code.split('\n')) else: c += indent(['pass']) - print(c) return c def addInput(self, i): self.inputs.append(i) @@ -302,15 +317,13 @@ menu.exec_(event.screenPos()) def itemChange(self, change, value): if change == self.ItemSelectedHasChanged: - for child in [self.sizer, self.buttonItemAddInput, self.buttonItemAddOutput]: + for child in [self.buttonItemAddInput, self.buttonItemAddOutput]: child.setVisible(value) return super(BlockItem, self).itemChange(change, value) def myDelete(self): - scene = self.scene() - if scene: - for p in self.inputs + self.outputs: - if p.connection: p.connection.myDelete() - scene.removeItem(self) + for p in self.inputs + self.outputs: + if p.connection: p.connection.myDelete() + self.scene().removeItem(self) def updateSize(self): rect = self.rect() h, w = rect.height(), rect.width() @@ -320,14 +333,17 @@ inp.setPos(0.0, y) for outp, y in zip(self.outputs, equalSpace(len(self.outputs), h)): outp.setPos(w, y) - def changeSize(self, p): - h = 20 if p.y() < 20 else p.y() - w = 40 if p.x() < 40 else p.x() + def setCenterAndSize(self, center, size): + self.changeSize(size.width(), size.height()) + p = QPointF(size.width(), size.height()) + self.setPos(center - p / 2) + def changeSize(self, w, h): + h = 20 if h < 20 else h + w = 40 if w < 40 else w self.setRect(0.0, 0.0, w, h) rect = self.label.boundingRect() self.label.setPos((w - rect.width()) / 2, (h - rect.height()) / 2) self.updateSize() - return QPointF(w, h) class EditorGraphicsView(QGraphicsView): def __init__(self, parent=None): @@ -477,6 +493,52 @@ def __init__(self): super(DiagramScene, self).__init__() self.startedConnection = None + self.selectionHandles = [ResizeSelectionHandle(i) for i in range(8)] + for h in self.selectionHandles: + self.addItem(h) + h.setVisible(False) + self.selectionChanged.connect(self.handleSelectionChanged) + def repositionAndShowHandles(self): + r = self.selectionRect + self.selectionHandles[Position.TOP_LEFT].setPos(r.topLeft()) + self.selectionHandles[Position.TOP].setPos(r.center().x(), r.top()) + self.selectionHandles[Position.TOP_RIGHT].setPos(r.topRight()) + self.selectionHandles[Position.RIGHT].setPos(r.right(), r.center().y()) + self.selectionHandles[Position.BOTTOM_RIGHT].setPos(r.bottomRight()) + self.selectionHandles[Position.BOTTOM].setPos(r.center().x(), r.bottom()) + self.selectionHandles[Position.BOTTOM_LEFT].setPos(r.bottomLeft()) + self.selectionHandles[Position.LEFT].setPos(r.left(), r.center().y()) + for h in self.selectionHandles: + h.setVisible(True) + def handleSelectionChanged(self): + [h.setVisible(False) for h in self.selectionHandles] + items = self.selectedItems() + items = [i for i in items if type(i) is BlockItem] + if items: + r = QRectF() + for i in items: + r = r.united(i.boundingRect().translated(i.scenePos())) + self.selectionRect = r + self.repositionAndShowHandles() + def sizerMoveEvent(self, handle, pos): + if handle.position == Position.TOP_LEFT: self.selectionRect.setTopLeft(pos) + elif handle.position == Position.TOP: self.selectionRect.setTop(pos.y()) + elif handle.position == Position.TOP_RIGHT: self.selectionRect.setTopRight(pos) + elif handle.position == Position.RIGHT: self.selectionRect.setRight(pos.x()) + elif handle.position == Position.BOTTOM_RIGHT: self.selectionRect.setBottomRight(pos) + elif handle.position == Position.BOTTOM: self.selectionRect.setBottom(pos.y()) + elif handle.position == Position.BOTTOM_LEFT: self.selectionRect.setBottomLeft(pos) + elif handle.position == Position.LEFT: self.selectionRect.setLeft(pos.x()) + else: + print('invalid position') + self.repositionAndShowHandles() + items = self.selectedItems() + items = [i for i in items if type(i) is BlockItem] + if items: + item = items[0] + # TODO resize more items! + item.setCenterAndSize(self.selectionRect.center(), self.selectionRect.size()) + blocks = property(lambda sel: [i for i in sel.items() if type(i) is BlockItem]) connections = property(lambda sel: [i for i in sel.items() if type(i) is Connection]) def setDict(self, d):