【问题标题】:how to redirect Wheel events of QWidget to QTextEdit如何将QWidget的Wheel事件重定向到QTextEdit
【发布时间】:2013-07-19 05:56:05
【问题描述】:

当鼠标不在QTextEdit上的时候转动鼠标滚轮,这种情况下滚动条不会移动,但是我还是想用鼠标滚轮来移动滚动条,请问如何实现这个功能呢? 我知道有些软件像 Microsoft Word 有这个功能。

我实现了这个功能如下,但是当你通过鼠标滚轮将滚动条移动到顶部或底部时,会出现错误:调用 Python 对象时超出了最大递归深度。 任何人都可以帮忙吗? 我的代码在这里http://codepad.org/1rq06qTk:

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *


class BoxLayout(QWidget):
    def __init__(self, parent=None):
        super(BoxLayout, self).__init__(parent)
        self.resize(100, 300)

        ok = QPushButton("OK")
        cancel = QPushButton("Cancel")
        self.textEdit = QTextEdit("This function returns true if the contents "
                                  "of the MIME data object, specified by source, "
                                  "can be decoded and inserted into the document. "
                                  "It is called for example when during a drag "
                                  "operation the mouse enters this widget and it "
                                  "is necessary to determine whether it is possible "
                                  "to accept the drag and drop operation.")

        vbox = QVBoxLayout()
        vbox.addWidget(self.textEdit)
        vbox.addWidget(ok)
        vbox.addWidget(cancel)
        self.setLayout(vbox)

#       self.textEdit.installEventFilter(self)


#    def eventFilter(self, obj, event):
#        if obj == self.textEdit:
#            if event.type() == QEvent.Wheel:
#                self.textEdit.wheelEvent(event)
#                return True
#            else:
#                return False
#        else:
#            return QMainWindow.eventFilter(self, obj, event)


    def wheelEvent(self, event):
        self.textEdit.wheelEvent(event)


app = QApplication(sys.argv)
qb = BoxLayout()
qb.show()
sys.exit(app.exec_())

【问题讨论】:

    标签: qt qt4 pyqt pyqt4 pyside


    【解决方案1】:

    您可以通过在任何将鼠标滚轮事件转发到 QTextEdit 的小部件上安装事件过滤器来做到这一点:

    很遗憾,我只能给你 C++ 代码,但它应该给你一个大致的概念。 另外,我使用的是 Qt 4.6.1。我在 Qt 4.8 及更高版本中检测到新的“滚动”事件类型,您可能需要在使用时更改代码。


    [编辑:]

    对于 TextEdit 滚动,由于某些原因,事件处理与标准方式不同: 将 Wheel 事件发送到 Text Edit 不会对其进行处理。 相反,某种私有事件过滤器通过QAbstractScrollArea::viewportEvent 调用wheelEvent,不幸的是,它受到了保护。

    长话短说,我们可以通过继承 QTextEdit 来伪造 Wheel Events:

    #include "MyTextEdit.h"
    
    MyTextEdit::MyTextEdit( QWidget* parent ) :
        QTextEdit( parent )
    {
    }
    
    void MyTextEdit::forwardViewportEvent( QEvent* event )
    {
        viewportEvent( event );
    }
    

    当使用它而不是默认的 QTextEdits 时,您可以像这样创建一个事件转发器:


    #ifndef WHEELEVENTFORWARDER_H
    #define WHEELEVENTFORWARDER_H
    
    #include <QtCore/QObject>
    
    class MyTextEdit;
    
    class WheelEventForwarder : public QObject
    {
        Q_OBJECT
    public:
        explicit WheelEventForwarder( MyTextEdit* target );
        ~WheelEventForwarder();
    
        bool eventFilter( QObject* obj, QEvent* event );
    
    private:
        MyTextEdit* _target;
    };
    
    #endif // WHEELEVENTFORWARDER_H
    

    WheelEventForwarder.cpp

    #include "WheelEventForwarder.h"
    #include "MyTextEdit.h"
    #include <QtCore/QEvent>
    #include <QtGui/QApplication>
    
    WheelEventForwarder::WheelEventForwarder( MyTextEdit* target ) :
        QObject(),
        _target( target )
    {
    }
    
    WheelEventForwarder::~WheelEventForwarder()
    {
        _target = NULL;
    }
    
    bool WheelEventForwarder::eventFilter( QObject* obj, QEvent* event )
    {
        Q_UNUSED( obj );
    
        static bool recursionProtection = false;
    
        if( recursionProtection ) return false;
    
        if( !_target ) return false;
    
        if( event->type() == QEvent::Wheel )
        {
            recursionProtection = true;
            _target->forwardViewportEvent( event );
            recursionProtection = false;
        }
    
        // do not filter the event
        return false;
    }
    

    然后你可以像这样安装一个事件过滤器:

    MainWindow.cpp(或任何更合适的地方):

    _wheelEventForwarder = new WheelEventForwarder( ui->textEdit );
    
    ui->centralwidget->installEventFilter( _wheelEventForwarder );
    

    有关更多信息,请参阅QObject::installEventFilter 的文档。

    【讨论】:

    • 我现在已经可以尝试并修复了一些编译错误 + 使类更可重用。然而,事件转发似乎没有按我预期的方式工作,我目前正在研究这个。
    • 我已经让它工作了,至少对于 Qt 4.6.1。查看我更新的答案(最好从头开始阅读,我必须更改很多)
    • recursionProtection 是一个静态变量,这意味着它的值将在所有函数调用之间共享。在调用forwardViewportEvent 之前,它被设置为true,因为这会调用另外几个以eventFilter 结尾的函数。 recursionProtection 然后防止函数被一遍又一遍地执行,否则会导致无限循环。如果需要,您也可以使用成员变量。
    • 谢谢,但是你是怎么发现 forwardViewportEvent 会导致再次调用 eventFilter 的呢?
    • 我已经在没有递归保护的情况下对其进行了测试,并得到了无限递归错误;-)
    【解决方案2】:

    您也可以采用这种方法:

    调用 QTextEdit.scroll(int,int) 函数在需要时滚动 qtextedit。 意味着您可以从小部件的 mouseScrollEvent 调用此函数

    【讨论】:

    • 但是没有名为“mouseScrollEvent”的函数
    • 以wheelEvent()命名。您可以使用此事件来执行您的任务
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    • 1970-01-01
    相关资源
    最近更新 更多