changeset 87:367006d423ae

Changed selection handles
author windel
date Fri, 23 Nov 2012 18:27:29 +0100
parents 317a73256bf0
children f3fe557be5ed
files python/apps/diagrameditor.py
diffstat 1 files changed, 81 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- 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):