[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