Search code examples
pythonpysideqgraphicsviewqgraphicssceneqgraphicslineitem

Making QGraphicsLineItem less fiddly to drag


As shown below, I've created a QGraphicsLineItem that is draggable. The problem though is that selecting the line is quite fiddly. I'd like to increase the "selection radius" of the line so that its becomes easier to drag

from PySide import QtGui, QtCore
import sys

class VerticalLine(QtGui.QGraphicsLineItem):
    def __init__(self, x , y0 , y1 , parent=None):
        super(VerticalLine, self).__init__(x , y0 , x , y1 , parent)
        self.setFlag(QtGui.QGraphicsLineItem.ItemIsMovable)
        self.setFlag(QtGui.QGraphicsLineItem.ItemSendsGeometryChanges)
        self.setCursor(QtCore.Qt.SizeAllCursor) 


class Editor(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Editor, self).__init__(parent)

        line = VerticalLine( 10 , 10 , 100 )
        scene = QtGui.QGraphicsScene()
        scene.addItem( line )
        view = QtGui.QGraphicsView()
        view.setScene( scene )

        self.setGeometry( 250 , 250 , 600 , 600 )
        self.setCentralWidget(view)
        self.show()

if __name__=="__main__":
    app=QtGui.QApplication(sys.argv)
    myapp = Editor()
    sys.exit(app.exec_()) 

Solution

  • You have to overwrite the shape() and boundingRect() methods so that they return a larger area, and for that QPainterPathStroker is used that creates an area using the line as a base and establishing a width.

    class VerticalLine(QtGui.QGraphicsLineItem):
        def __init__(self, x , y0 , y1 , parent=None):
            super(VerticalLine, self).__init__(x , y0 , x , y1 , parent)
            self.setFlag(QtGui.QGraphicsLineItem.ItemIsMovable)
            self.setFlag(QtGui.QGraphicsLineItem.ItemSendsGeometryChanges)
            self.setCursor(QtCore.Qt.SizeAllCursor) 
    
        def shape(self):
            path = super(VerticalLine, self).shape()
            stroker = QtGui.QPainterPathStroker()
            stroker.setWidth(20)
            return stroker.createStroke(path)
    
        def boundingRect(self):
            return self.shape().boundingRect()