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 *
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)
def init(self, *args):
self.slot = SlotItem(QPointF(1,1))
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)
def init(self, *args):
self.scene = DiagramScene()
self.view = QGraphicsView(self.scene)
# 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?
app = QApplication(args)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 500, 40)
# Qt Main loop sys.exit(app.exec_())
if name == "main":
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.