Кажется, это старая ошибка, которая так и не была устранена: setCursor на QGraphicsView не работает при добавлении QWidget на QGraphicsScene
Возможный обходной путь, но он далек от perfect.
Прежде всего, вы должны учитывать, что при работе с QGraphicsScene и его view [s] нелегко работать с событиями мыши и прокси виджетов, в основном из-за множественных вложенных уровней событий и взаимодействия между фактический вид (и его родитель, вплоть до окна верхнего уровня) и сам прокси, который является абстракцией виджета, который вы добавили в сцену. Хотя разработчики Qt проделали огромную работу, чтобы сделать его максимально прозрачным, в какой-то момент вы, вероятно, столкнетесь с неожиданным или нежелательным поведением, которое обычно трудно исправить или обойти, и это также потому, что графическая сцена может быть визуализирована в больше, чем одно представление.
Помимо вышеупомянутой ошибки, вы должны учитывать, что графическое представление использует QWidget.setCursor
внутри себя всякий раз, когда любой из его элементов вызывает setCursor
для себя, и так как представление является очень сложным widget, в какой-то момент он может даже попытаться «восстановить» курсор, если он считает необходимым (даже если не должен).
Наконец, некоторые события, которые также имеют отношение к фокусу может стать препятствием для всего этого.
Первый обходной путь - установить курсор на само представление (или, что лучше, на видовой экран вида, который является фактическим виджетом, отображающим содержимое сцены). Чтобы убедиться в этом, нам, очевидно, необходимо проверить, находится ли курсор внутри холста.
К сожалению, из-за написанной выше обработки событий это может стать немного грязным, поскольку некоторые события даже задерживаются по крайней мере на цикл внутри основного цикла событий Qt; В результате, при установке курсора в первый раз может работать , его повторная установка может не , и даже если это произойдет, возможно, что курсор не будет применен, пока мышь перемещается как минимум на один пиксель.
В качестве второго обходного пути нам нужен фильтр событий, чтобы обойти все это и проверять курсор всякий раз, когда мышь перемещается в пределах полей области просмотра.
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# ...
self.show()
empty = QPixmap(1240, 1748)
empty.fill(QColor(Qt.darkGray))
self.canvas.newPixmap(empty)
# install an event filter on the view's viewport;
# this is very, *VERY* important: on the *VIEWPORT*!
# if you install it on the view, it will *not* work
self.MainView.viewport().installEventFilter(self)
def insideCanvasRect(self, pos):
canvasRect = self.canvas.rect()
# translate the canvas rect to its top level window to get the actual
# geometry according to the scene; we can't use canvas.geometry(), as
# geometry() is based on the widget's parent coordinates, and that
# parent could also have any number of parents in turn;
canvasRect.translate(self.canvas.mapTo(self.canvas.window(), QPoint(0, 0)))
# map the geometry to the view's transformation, which probably uses
# some scaling, but also translation *and* shearing; the result is a
# polygon, as with shearing you could transform a rectangle to an
# irregular quadrilateral
polygon = self.MainView.mapFromScene(QRectF(canvasRect))
# tell if the point is within the resulting polygon
return polygon.containsPoint(pos, Qt.WindingFill)
def eventFilter(self, source, event):
if source == self.MainView.viewport() and (
(event.type() == QEvent.MouseMove and not event.buttons()) or
(event.type() == QEvent.MouseButtonRelease)
):
# process the event
super(MainWindow, self).eventFilter(source, event)
if self.insideCanvasRect(event.pos()):
source.setCursor(self.canvas.cursor())
else:
source.unsetCursor()
# usually a mouse move event within the view's viewport returns False,
# but in that case the event would be propagated to the parents, up
# to the top level window, which might reset the *previous* cursor
# at some point, no matter if we try to avoid that; to prevent that
# we return True to avoid propagation.
# Note that this will prevent any upper-level filtering and *could*
# also create some issues for the drag and drop framework
if event.type() == QEvent.MouseMove:
return True
return super(MainWindow, self).eventFilter(source, event)
def keyPressEvent(self, e):
# send the canvas a fake leave event
QApplication.sendEvent(self.canvas, QEvent(QEvent.Leave))
key = e.key()
if key == Qt.Key_C:
self.canvas.setCutCursor()
elif key == Qt.Key_N:
self.canvas.setNormalCursor()
elif key == Qt.Key_S:
self.canvas.setSelectionCursor()
pos = self.canvas.rect().center()
event = QEnterEvent(pos, self.canvas.mapTo(self.canvas.window(), pos), self.canvas.mapToGlobal(pos))
# send a fake enter event (mapped to the center of the widget, just to be sure)
QApplication.sendEvent(self.canvas, event)
# if we're inside the widget, set the view's cursor, otherwise it will not
# be set until the mouse is moved
if self.insideCanvasRect(self.MainView.viewport().mapFromGlobal(QCursor.pos())):
self.MainView.viewport().setCursor(self.canvas.cursor())