【问题标题】:How to prevent scrolling while 'ctrl' is pressed in PyQt5?在 PyQt5 中按下“ctrl”时如何防止滚动?
【发布时间】:2021-11-02 11:08:49
【问题描述】:

使用 PyQt5 我正在 QGraphicsView 中查看图像。我希望能够在按下 ctrl 并使用鼠标滚轮时放大/缩小。我有这个工作,但是如果图像太大,并且有滚动条,它会忽略缩放功能,直到您滚动到顶部或底部。

如何将其修复为按下 ctrl 时不滚动的位置,同时允许它放大/缩小。

from PyQt5.QtWidgets import QFileDialog, QLineEdit, QWidget, QPushButton, QApplication, QVBoxLayout, QLabel, QGraphicsView, QGraphicsPixmapItem, QGraphicsScene
from PyQt5.QtCore import pyqtSignal, Qt
from pdf2image import convert_from_path
from PIL import ImageQt
import sys

class step1(QWidget):
    changeViewSignal = pyqtSignal()
    
    def __init__(self, parent=None):
        super(step1, self).__init__(parent)
        self.name = QLineEdit(self)
        self.fileBtn = QPushButton("Select file", self)
        self.nextBtn = QPushButton("Next", self)
        self.graphicsView = QGraphicsView()
        # self.graphicsView.setFrameShadow(QFrame.Raised)
        # self.graphicsView.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContentsOnFirstShow)
        self.graphicsView.setHorizontalScrollBarPolicy()

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.name)
        self.layout.addWidget(self.fileBtn)
        self.layout.addWidget(self.nextBtn)
        self.layout.addWidget(self.graphicsView)

        self.fileBtn.clicked.connect(self.convert_file)

    def wheelEvent(self, event):
        modifiers = QApplication.keyboardModifiers()
        if modifiers == Qt.ControlModifier:
            self.graphicsView.scrollContentsBy(0,0)
            x = event.angleDelta().y() / 120
            if x > 0:
                self.graphicsView.scale(1.05, 1.05)
            elif x < 0:
                self.graphicsView.scale(.95, .95)

    def convert_file(self):
        fname = QFileDialog.getOpenFileName(self, 'Open File', 'c:\\', "PDF Files (*.pdf)")
        if len(fname[0]) > 0:
            pages = convert_from_path(fname[0])
            images = []
            qimage = ImageQt.toqpixmap(pages[0])
            item = QGraphicsPixmapItem(qimage)
            scene = QGraphicsScene(self)
            scene.addItem(item)
            self.graphicsView.setScene(scene)
        

       

if __name__ == '__main__':
   app = QApplication(sys.argv)
   ex = step1()
   ex.show()
   sys.exit(app.exec_())

【问题讨论】:

    标签: python python-3.x pyqt pyqt5


    【解决方案1】:

    滚动首先由 QGraphicsView 处理,然后再传播到您正在重新实现wheelEvent 的父窗口小部件。这就是为什么当有空间滚动时,会根据正常的 QGraphicsView 行为发生滚动。

    一种解决方案是继承 QGraphicsView 并在那里重新实现 wheelEvent

    class GraphicsView(QGraphicsView):
    
        def wheelEvent(self, event):
            if event.modifiers() & Qt.ControlModifier:
                x = event.angleDelta().y() / 120
                if x > 0:
                    self.scale(1.05, 1.05)
                elif x < 0:
                    self.scale(.95, .95)
            else:
                super().wheelEvent(event)
    

    然后在这里使用子类名:

    self.graphicsView = GraphicsView()
    

    【讨论】:

      【解决方案2】:

      除了alec 提出的正确解决方案之外,还可以选择使用事件过滤器,这对于在 Designer 中创建的 UI 非常有用,而无需使用提升的小部件。

      要记住的重要方面是事件过滤器必须安装在视图的viewport()(实际渲染场景内容并且可能滚动的小部件)上,因为那个是接收滚轮事件的小部件:输入事件总是发送到鼠标鼠标(或有键盘焦点)[1]的小部件,如果事件没有被处理,可能会传播给他们的父母[2]

      class step1(QWidget):
          def __init__(self, parent=None):
              # ...
              self.graphicsView.viewport().installEventFilter(self)
      
          def eventFilter(self, source, event):
              if event.type() == event.Wheel and event.modifiers() & Qt.ControlModifier:
                  x = event.angleDelta().y() / 120
                  if x > 0:
                      self.graphicsView.scale(1.05, 1.05)
                  elif x < 0:
                      self.graphicsView.scale(.95, .95)
                  return True
              return super().eventFilter(source, event)
      

      返回True表示viewport已经处理了事件,应该传播给父级; QGraphicsView 基于 QAbstractScrollArea,如果视口未处理滚轮事件,它将调用视口父级(图形视图)的基本 wheelEvent 实现,默认情况下会发布事件到滚动条。如果过滤器返回True,它将避免传播,从而防止滚动。

      请注意,除非您真的知道自己在做什么,否则不应使用scrollContentsBy;作为文档explains:«调用此函数以编程方式滚动是一个错误,请改用滚动条»。

      [1] 鼠标事件总是被发送到鼠标下方的 topmost 小部件,除非 modal 子窗口处于活动状态,或者存在 mouse grabber,这是一个已接收到鼠标按钮按下事件但尚未接收到鼠标按钮释放事件的小部件,或者是在其上显式调用 grabMouse() 的小部件。键盘事件总是发送到具有当前焦点的活动窗口的小部件,或已显式调用grabKeyboard() 的小部件。
      [2]“处理的事件”可能是一个令人困惑的概念:它并不意味着小部件实际上对事件“做”某事,也不意味着它没有,无论事件是否变为 接受忽略。小部件可以“忽略”一个事件并仍然以某种方式对其做出反应:例如,您可能需要通知用户该事件已接收,但无论如何都要让父级管理它。

      【讨论】:

        猜你喜欢
        • 2012-06-26
        • 1970-01-01
        • 2012-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多