[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