Esempio n. 1
0
class ObjectWidget(QWidget):
        """The base class for all circuit object widgets. It implements a lot of basic
        functionality, including selecting, dragging, connection/disconnection and attribute storage.
        
        Each widget has a number of anchor points where connections can be made. 
        Subclasses must set these up in their constructors, by changing the properties
        self._anchorPoints, self._connections (set it to empty lists initially)
        and self._maxConnections. See NodeWidget and friends below for examples of
        subclasses.
        
        SIGNALS:
                selected() - emitted when this object is selected
                unselected()
                connectionsChanged()
                clicked(int, QPoint) - emitted when the object is clicked,
                                       with distance from nearest anchor and which
                                       anchor it is.
                moved(QPoint) - emitted when the object moves.
                flipped() - emitted when the object flips orientation
        """
        def __init__(self, parent=None):
                QWidget.__init__(self, parent)
                self._orientation = 'h'
                self._isSelected = False
                self._isDragging = False
                self._anchorPoints = [(0,0)]
                self._connections = {self._anchorPoints[0]: []}
                self._maxConnections = {self._anchorPoints[0]: 0}
                self._attributes = {'nickname': ''}
                
                # attribute model
                self._attrmodel = ObjectAttributeModel(self)
                
                # slot connections
                QObject.connect(self, SIGNAL("selected()"), self, SLOT("update()"))
                QObject.connect(self, SIGNAL("unselected()"), self, SLOT("update()"))
                
        def __getitem__(self, attr):
                """Get an attribute of the object."""
                return self._attributes[attr]
                
        def __setitem__(self, attr, value):
                """Set an attribute of the object."""
                self._attributes[attr] = value
                
        def attributes(self):
                """Returns the list of available attributes.
                
                attributes() -> list<string>
                """
                return self._attributes.keys()
                
        def anchorPoints(self):
                """Returns the list of available anchorpoints.
                
                anchorPoints() -> list<tup>
                """
                return self._anchorPoints
                
        def connections(self, ancPoint=None):
                """If given an anchor point, this functionr returns a list of 
                connections associated with that anchor point. Otherwise, it will
                return all connections associated with this object.
                
                connections are of the form: list<tuple<object, anchor>, tuple<object,anchor>>
                
                connections([tup]) -> list<connections>
                """
                if ancPoint:
                        return [[(self, ancPoint), (other, other.anchorOf(self))] for other in self._connections[ancPoint]]
                else:
                        cons = []
                        for ap in self._anchorPoints:
                                cons = cons + self.connections(ap)
                        return cons
                
        def anchorOf(self, other):
                """Gets the anchor point that connects this object to other.
                anchorOf(object) -> QPoint
                """
                for x in self._connections:
                        if other in self._connections[x]:
                                return x
                return None
                
        def canConnect(self, ancPoint):
                """Is there space for another connection on the given anchor point?
                
                canConnect(QPoint) -> bool
                """
                return (len(self._connections[ancPoint]) < self._maxConnections[ancPoint] or self._maxConnections[ancPoint] == -1)
                
        def connectTo(self, ancPoint, object):
                """Connects this object to another via the given anchor point, if possible,
                in a one-way fashion.
                
                connectTo(QPoint, object) -> void
                """
                if self.canConnect(ancPoint):
                        self._connections[ancPoint].append(object)
                        self.emit(SIGNAL("connectionsChanged()"))
                        
        def connect(self, ancPoint, object, otherAncPoint):
                """Connects this object to another via the two given anchors, if possible,
                in a two-way fashion.
                
                connect(QPoint, object ,QPoint) -> void
                """
                if self.canConnect(ancPoint) and object.canConnect(otherAncPoint):
                        self.connectTo(ancPoint, object)
                        object.connectTo(otherAncPoint, self)
                        
        def disconnectFrom(self, ancPoint, object):
                """Destroys one end of a connection towards object via anchor point."""
                if object in self._connections[ancPoint]:
                        self._connections[ancPoint].remove(object)
                        self.emit(SIGNAL("connectionsChanged()"))
                        
        def disconnect(self, ancPoint, object, otherAncPoint):
                """Destroys an entire connection towards object's otherAncPoint 
                via this object's ancPoint."""
                self.disconnectFrom(ancPoint, object)
                object.disconnectFrom(otherAncPoint, self)
                
        def mouseDoubleClickEvent(self, event):
                """Handles a mouse double click by opening an attribute dialog"""
                self._attrdlg = AttributeDialog(self._attrmodel)
                self._attrdlg.show()
                
        def mousePressEvent(self, event):
                """Handles a mouse press event on the object widget. Prepares to
                start the drag-movement process, and attempts to select the widget."""
		self._isDragging = True
		self._dragLastPos = QPoint(event.globalX(), event.globalY())
		self._dragStartPos = QPoint(self.x(), self.y())
                
                # find nearest anchor point to where the user clicked
                anchorPoints = [QPoint(x,y) for x,y in self._anchorPoints]
                dragPoint = QPoint(event.x(), event.y())
                leastDist = (dragPoint - anchorPoints[0]).manhattanLength()
                leastPoint = anchorPoints[0]
                for p in anchorPoints:
                        dist = (dragPoint - p).manhattanLength()
                        if dist < leastDist:
                                leastDist = dist
                                leastPoint = p
                self._dragClosestAnchor = leastPoint
                
                # emit the Qt signal
                self.emit(SIGNAL("clicked(int,QPoint)"), leastDist, leastPoint)
		#self.parent().selectMe(self)
                
        def mouseMoveEvent(self, event):
                """Handles mouse movement on the widget. If we're in drag mode, ie
                a button is held down, the widget will move by snapped intervals."""
		if self._isDragging:
			curPos = QPoint(self.x(), self.y())
			newPos = self._dragStartPos + (event.globalPos() - self._dragLastPos)
                        snapPos = self.parent().snap(newPos)
			self.move(snapPos)
			self.emit(SIGNAL("moved(QPoint)"), snapPos)
			#self.lastPos = event.globalPos()
                        
        def mouseReleaseEvent(self, event):
		self._isDragging = False
                
        def flip(self):
                """Flips the object's orientation over."""
                self._orientation = {'h': 'v', 'v': 'h'}[self._orientation]
                self.update()
                self.emit(SIGNAL("flipped()"))
                
        def orientation(self):
                """Returns the object's present orientation.
        
                orientation() -> char (one of 'h' or 'v')
                """
                return self._orientation
                
        def selected(self):
                """Returns True if the object is selected, otherwise False."""
                return self._isSelected
                
        def select(self):
                """Sets this object as selected."""
                self._isSelected = True
                self.emit(SIGNAL("selected()"))
                
        def unselect(self):
                """Sets this object as unselected."""
                self._isSelected = False
                self.emit(SIGNAL("unselected()"))
                
        def drawSelectedOutline(self, painter):
                """A convenience function for subclasses to paint a red box around
                the widget when it is selected.
                
                drawSelectedOutline(QPainter) -> void
                """
                
                if self._isSelected:
			painter.setPen(Qt.red)
			painter.drawRect(QRect(0,0,self.width(), self.height()))
Esempio n. 2
0
 def mouseDoubleClickEvent(self, event):
         """Handles a mouse double click by opening an attribute dialog"""
         self._attrdlg = AttributeDialog(self._attrmodel)
         self._attrdlg.show()