更改事件键上的光标形状

问题描述 投票:2回答:1

我正在尝试在按键事件上更改光标形状:

  • 当我按'C'时,我想显示一个LineCursor,
  • 当我按'S'时,我想显示一个CrossCursor,然后
  • 当我按'N'时,我想显示标准的ArrowCursor。

仅当光标离开画布并返回画布时,它才会改变,但是如果光标停留在画布上则不会。画布上的self.update()无效

这里是重现该问题的代码:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setObjectName("MainWindow")
        self.resize(942, 935)
        self.centralwidget = QWidget(self)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.MainView = QGraphicsView(self.centralwidget)
        self.MainView.setObjectName("MainView")
        self.horizontalLayout.addWidget(self.MainView)
        self.setCentralWidget(self.centralwidget)
        self.setWindowTitle("MainWindow")

        self.scene = QGraphicsScene( 0.,0., 1240., 1780. )
        self.canvas = Canvas()

        self.widget = QWidget()
        box_layout = QVBoxLayout()
        self.widget.setLayout(box_layout)
        box_layout.addWidget(self.canvas)
        self.scene.addWidget(self.widget)
        self.MainView.setScene(self.scene)
        self.MainView.setRenderHints(QPainter.Antialiasing)
        self.MainView.fitInView(0, 0, 45, 55, Qt.KeepAspectRatio)

        self.show()

        empty = QPixmap(1240, 1748)
        empty.fill(QColor(Qt.white))
        self.canvas.newPixmap(empty)

    def keyPressEvent(self, e):
        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()


class Canvas(QLabel):
    def __init__(self):
        super().__init__()
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setSizePolicy(sizePolicy)
        self.setAlignment(Qt.AlignLeft)
        self.setAlignment(Qt.AlignTop)

    def newPixmap(self, pixmap):
        self.setPixmap(pixmap)

    def setCutCursor(self):
        newCursor = QPixmap(500,3)
        newCursor.fill(QColor("#000000"))
        self.setCursor(QCursor(newCursor))

    def setSelectionCursor(self):
        self.setCursor(Qt.CrossCursor)

    def setNormalCursor(self):
        self.setCursor(QCursor(Qt.ArrowCursor))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    sys.exit(app.exec_())
python pyqt pyqt5 keyevent
1个回答
0
投票

似乎是一个从未解决的老错误:setCursor on QGraphicsView don't work when add QWidget on the QGraphicsScene

[可能有一种解决方法,但这远非完美。首先,您必须考虑到,在处理QGraphicsScene及其视图时,处理鼠标事件和窗口小部件代理并不容易,这主要是因为事件的多个嵌套级别以及实际视图(及其视图之间的交互)父级,直到顶层窗口)和代理本身,这是您添加到场景中的小部件的抽象。虽然Qt开发人员做了很多工作以使其尽可能透明,但有时您可能会遇到一些意料之外的或不希望有的行为,这些行为通常很难修复或解决,这也是因为图形场景可以在其中可视化不只是一个视图。

[除了上述错误之外,您还必须考虑到图形视图在其任何项自己调用QWidget.setCursor时都会在内部使用setCursor,并且由于该视图是非常复杂的小部件,因此有时甚至可能尝试使用如果光标认为应该(即使不应该)“恢复”它。最后,一些与焦点有关的事件可能会成为所有事情的方式。

第一个解决方法是将光标设置到视图本身(或者更好的是,视图的视口,它是显示场景内容的实际小部件)。为了确保这一点,我们显然需要检查光标是否在画布内。

[不幸的是,由于上面所述的事件处理,这可能变得有些混乱,因为某些事件甚至在主Qt事件循环内甚至延迟了至少一个周期;结果是,在第一次设置光标[时,可能再次设置[[not,即使可以,甚至在至少移动鼠标之前,光标都不会被应用。一个像素第二种解决方法是,我们需要一个事件过滤器来绕过所有这些事件,并在鼠标在视口边界内移动时检查光标。

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())
© www.soinside.com 2019 - 2024. All rights reserved.