[PyQt] setCursor does not seem to work as expected in graphicsScene/View

Baz Walter bazwal at ftml.net
Wed Aug 25 19:05:50 BST 2010


On 25/08/10 09:54, Christopher M. Nahler wrote:
> I'll take it as an "if all else fails" solution :-) not giving hope up now!
>
> I have changed the item cursors and view cursors to be differnt to see
> which one wins as sometimes it looked as if it was working. But it turns
> out its the item cursors that wins.
>
> After reading more on cursors I have found that application override
> cursor works with a stack. Is this also the case with the "normal" item
> cursors? Do I need to unset cursors? I see a reference to a
> unsetCursor() function but cannot find details to it.

attempting to get this to work by setting the cursor on the view and/or 
the items can be quite, um, "challenging". it reminds me of one of those 
puzzles where you have to roll several ball bearings around at once 
trying to get them all to land in little holes :)

the main problem seems to be that setting the cursor on any item may 
reset the cursor on the view, depending on whether the mouse is 
currently over an item or not (amongst other things).

one way to solve this would be to avoid setting the cursor on the items 
altogether, and to use mouse/keyboard events on the view to dynamically 
control the current cursor shape.

here's an implementation that hopefully does what you want:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MyRect(QGraphicsRectItem):

     def __init__(self, parent=None, scene=None):
         # init parent
         super().__init__(parent, scene)

         # set flags
         self.setFlag(QGraphicsItem.ItemIsSelectable, True)
         self.setFlag(QGraphicsItem.ItemIsMovable, True)
         self.setFlag(self.ItemSendsGeometryChanges, True)
         self.setAcceptHoverEvents(True)

     def paint(self, painter, option, widget):
         if self.isSelected():
             painter.setPen(QPen(Qt.black, 1, Qt.DotLine))
         else:
             painter.setPen(QPen(Qt.black, 1, Qt.SolidLine))

         painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
         painter.drawRect(self.rect())

class MyView(QGraphicsView):

     def __init__(self, parent=None):
         super().__init__(parent)
         self.setMouseTracking(True)
         self.scale(1,1)
         self.startPos = None

     def setSelectMode(self):
         print("view setting select mode")
         self.scene().setViewMode(MyScene.SELECTMODE)

     def setEditMode(self):
         print("view setting edit mode")
         self.scene().setViewMode(MyScene.EDITMODE)
         if len(self.scene().selectedItems()) > 1:
             self.scene().clearSelection()

     def setDrawMode(self):
         print("view setting draw mode")
         self.scene().setViewMode(MyScene.DRAWMODE)
         self.scene().clearSelection()

     def drawBackground(self, painter, rect):
         # draw a rect in size of sceneRect

         painter.setPen(QPen(Qt.red, 0, Qt.NoPen))
         painter.setBrush(QBrush(Qt.lightGray, Qt.SolidPattern))
         painter.drawRect(self.scene().sceneRect())

     def mousePressEvent(self, mouseEvent):
         print("view mousePress")

         curPos = mouseEvent.pos()
         self.startPos = self.mapToScene(curPos)

         if self.scene().viewMode() == MyScene.DRAWMODE:
             self.scene().newItem = MyRect(scene=self.scene())
             self.scene().newItem.setRect(QRectF(self.startPos, 
QSizeF(0, 0)))
             self.scene().newItem.setSelected(True)

         else:
             super().mousePressEvent(mouseEvent)
	
     ### generic method for setting the cursor
     def updateCursor(self):
         m = self.scene().viewMode()
         if m == MyScene.SELECTMODE:
             item = self.itemAt(self.mapFromGlobal(QCursor.pos()))
             if item is None:
                 self.viewport().setCursor(Qt.ArrowCursor)
             else:
                 self.viewport().setCursor(Qt.OpenHandCursor)
         elif m == MyScene.EDITMODE:
             self.viewport().setCursor(Qt.ForbiddenCursor)
         elif m == MyScene.DRAWMODE:
             self.viewport().setCursor(Qt.CrossCursor)

     def mouseMoveEvent(self, mouseEvent):

         ### dynamically set the cursor
         self.updateCursor()

         if self.scene().viewMode() == MyScene.DRAWMODE:
             if self.scene().newItem:
                 curPos = self.mapToScene(mouseEvent.pos())
                 newRectF = QRectF(self.startPos, curPos)
                 if newRectF != self.scene().newItem.rect():
                     self.scene().newItem.setRect(newRectF.normalized())
         else:
             super().mouseMoveEvent(mouseEvent)

     def mouseReleaseEvent(self, mouseEvent):
         print("view mouseRelease")

         if self.scene().newItem:
             # delete item if zero height or width
             if (self.scene().newItem.rect().width() == 0
                 or self.scene().newItem.rect().height() == 0):
                 self.scene().removeItem(self.scene().newItem)
                 del self.scene().newItem

         self.startPos = None
         self.scene().newItem = None
         super().mouseReleaseEvent(mouseEvent)

         ### the default behaviour may unset the cursor, so reset it here
         self.updateCursor()

     def keyPressEvent(self, keyEvent):
         if keyEvent.key() == Qt.Key_F1:
             self.setSelectMode()
             self.updateCursor()

         elif keyEvent.key() == Qt.Key_F2:
             self.setEditMode()
             self.updateCursor()

         elif keyEvent.key() == Qt.Key_F3:
             self.setDrawMode()
             self.updateCursor()

         elif keyEvent.key() == Qt.Key_Delete:
             if self.scene().viewMode() == MyScene.SELECTMODE:
                 items = self.scene().selectedItems()
                 if len(items):
                     for item in items:
                         self.scene().removeItem(item)
                         del item

class MyScene(QGraphicsScene):

     SELECTMODE, EDITMODE, DRAWMODE = (0, 1, 2)
     validViewModes = [SELECTMODE, EDITMODE, DRAWMODE]

     def __init__(self, parent=None):
         super().__init__(parent)

         self._viewMode = MyScene.SELECTMODE
         self.newItem = None

         self.setSceneRect(-300, -200, 600, 400)

         # add some item
         someRect = MyRect(scene=self)
         someRect.setRect(QRectF(0, 0, 160, 80))

         # add another item
         anotherRect = MyRect(scene=self)
         anotherRect.setRect(QRectF(-80, -40, 80, 160))

     def setViewMode(self, value):
         if value != self._viewMode:
             if value in MyScene.validViewModes:
                 self._viewMode = value
             else:
                 raise ValueError("invalid view mode")

     def viewMode(self):
         return self._viewMode

class MainWindow(QMainWindow):

     def __init__(self, parent=None):
         # call parent init
         super().__init__(parent)

         # setup scene object
         self.scene = MyScene()

         # setup view object
         self.view = MyView()

         # connect scene to view
         self.view.setScene(self.scene)

         # create layout
         layout = QVBoxLayout()

         # add view to layout
         layout.addWidget(self.view)

         # set the margin of the object in the layout
         layout.setContentsMargins(0, 0, 0, 0)

         # create the central widget
         self.widget = QWidget()

         # lay it out
         self.widget.setLayout(layout)

         # set it to central
         self.setCentralWidget(self.widget)

if __name__ == "__main__":

     import sys

     # setup application object
     app = QApplication(sys.argv)

     # create (parent) main window
     mainWindow = MainWindow()
     mainWindow.setWindowTitle("testScene")
     mainWindow.show()

     # run application object
     sys.exit(app.exec_())


More information about the PyQt mailing list