Demonstrate picking edges of QGraphicItems



  • Just something I worked out. Python program that demonstrates mouseover picking by edge instead of by inside.

    @'''
    Demonstrate edge picking of graphics items in Qt QGraphicsFramework.

    Here picking is on mouseover, not on mouse press.

    Lloyd Konneker 2011
    '''

    from PySide.QtCore import *
    from PySide.QtGui import *
    import sys

    class SlotItem(QGraphicsRectItem):
    SCALE = 40.0

    def __init__(self, pos):
        super(SlotItem, self).__init__()
        self.setRect(pos.x(), pos.y(), 1, 1)    # Unit rect, 1x1
        self.scale(self.SCALE, self.SCALE)      # Scaled to 40x40
        
        # Create a path to pick my edge
        
        # Need a stroker
        stroker = QPainterPathStroker()
        
        '''
        The item is in unit coordinates.
        The width of the brush (the ink) of the stroker
        should be smaller than the unit dimension of the item shape.
        This sets the sensitivity of edge picking.
        If too small, could be floating point precision errors,
        or other inaccuracies in the algorithm used by Qt for path.intersects(point)???
        More generally, it should be a small fraction of the size of the item.
        '''
        stroker.setWidth(.001)
        
        '''
        Stroke the shape, i.e. ink it with a brush, creating path around the ink.
        The shape is the unfilled outline of the item, that is, the edge.
        The stroked path for example is a toroid, a donut.
        The inside of the toroid is contained within the shape
        and the outside of the toroid contains the shape.
        The ink fills the toroid.
        The toroid is tight to the ink width stroked.
        '''
        self.edgePickingPathLCS = stroker.createStroke(self.shape())
        print "Edge path in local coordinates", self.edgePickingPathLCS
        
        '''
        Map entire path to scene coords.
        The coordinate system of the path must match the coordinate system of points
        we will intersect with.
        '''
        self.edgePickingPathSCS = self.transform().map(self.edgePickingPathLCS)
        print "Edge path in sapp.exec_(**args)cene coordinates", self.edgePickingPathSCS
        
    def inPath(self, event):
      '''
      The event has methods returning coords in local, scene, or screen coord systems.
      Since Qt did not dispatch to this item, the local coords are suspect.
      Here we work in scene coords.
      If Qt dispatched the event to an item's mouseMoveEvent,
      event.pos() yields a point in local coords,
      and we would instead do self.edgePickingPathLCS.intersects(QPainterPath(event.pos()))
      '''
      scenePoint = QPointF(event.scenePos())
      
      # Need a small path for a point since there is no method intersects(QPointF)
      pointPath = QPainterPath(scenePoint)
      
      # Does the point lie in the fill of the stroked path
      # that is, between the inner and outer edges of the toroid?
      return self.edgePickingPathSCS.intersects(pointPath)
    

    class DiagramScene(QGraphicsScene):
    def init(self, *args):
    QGraphicsScene.init(self, *args)
    self.slot = SlotItem(QPointF(1,1))
    self.addItem(self.slot)

        text = QGraphicsTextItem("Picking a graphics item by its edge: item is red if mouse in edge.")
        text.setTextInteractionFlags(Qt.TextEditorInteraction)
        self.addItem(text)
        
    def mouseMoveEvent(self, event):
      # Do our own dispatch,app.exec_(**args) always to the item
      if self.slot.inPath(event):
        self.slot.setBrush(Qt.red)
      else:
        self.slot.setBrush(Qt.green)
    

    class MainWindow(QMainWindow):
    def init(self, *args):
    QMainWindow.init(self, *args)
    self.scene = DiagramScene()
    self.view = QGraphicsView(self.scene)
    self.view.setRenderHint(QPainter.Antialiasing)
    # This is not needed to get mouse events in the view, which is a widget.
    # If graphic items want sceneMouseEvents, this must be set on the graphic items?
    # self.view.setMouseTracking(True);
    self.setCentralWidget(self.view)

    def main(args):
    app = QApplication(args)
    mainWindow = MainWindow()
    mainWindow.setGeometry(100, 100, 500, 40)
    mainWindow.show()

    # Qt Main loop
    sys.exit(app.exec_())
    

    if name == "main":
    main(sys.argv)@



  • To the moderator: I don't understand why you put this in the bindings forum. This is not about language bindings.

    Don't be parochial about C++. One way of looking at it is that C++ is just another language binding (a null binding) to the library, which happens to be written in C++.

    The post is an algorithm, a tutorial. It could just have well been in pseudo-code.

    The implied thread topic is why Qt does not have an in_stroke() method as in Cairo? Then, yes, it might be in the wrong forum.



  • We usually move all non C++ code to the Language Bindings subforum.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.