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

Christopher M. Nahler christopher.nahler at papermodels.at
Thu Aug 26 08:45:38 BST 2010


  Thank you Baz! You pointed me to the solution of my problem ... and it 
was easier than your solution :-)

I did not know of *self.viewport()* in the view class.

Updating the cursor of the view with self.viewport().setCursor() instead 
of just self.setCursor() did the trick.

On 25.08.2010 20:05, Baz Walter wrote:
> 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