【问题标题】:PyQt: Pass `mousePressEvent` to original widget when overidding `mousePressEvent` method in base classPyQt:在基类中覆盖`mousePressEvent`方法时将`mousePressEvent`传递给原始小部件
【发布时间】:2019-01-30 01:16:15
【问题描述】:

我正在尝试为小部件创建一个全局上下文帮助系统。所有小部件都可以使用 ContextHelpBase 类进行扩展,并具有向上下文帮助显示小部件发送信号所需的所有逻辑。

这个想法是当用户点击一个小部件时,它会显示一些上下文帮助。所以我重载了mousePressEvent 以发送信号,但现在正常的按钮和 QComboBox 行为不起作用,因为我假设我没有在正常事件处理程序上传递信号,因为我正在覆盖它。

from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QPushButton, QComboBox, QHBoxLayout, QVBoxLayout
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot

class ContextHelpSignals(QObject):
    """ Used to send signals globally through the application
        Eventually will be a Global Singelton
    """
    textHelp = pyqtSignal(str)

    def __init__(self):
        super(ContextHelpSignals, self).__init__()

#Eventually will be a Global Singleton
_contextHelp = ContextHelpSignals()

class ContextHelpBaseClass(QObject):
    """All Widget that have context help inherits this class"""
    def __init__(self, **kw):
        super(ContextHelpBaseClass, self).__init__()
        self.helpText = None

    def mousePressEvent(self, event):
        """ THIS DISABLE WIDGETS NORMAL CLICK BEHAVIOR """
        _contextHelp.textHelp.emit(self.helpText)
        # How can emit a signal and then pass this event to the normal widget
        print(type(super()))

    def SetHelpText(self, helpText):
        self.helpText = helpText

class ContexHelpDisplay(QLabel):
    """Dislpay Context Help frow widgets that have Context Help"""
    def __init__(self, text):
        super(ContexHelpDisplay, self).__init__()
        self.setText(text)
        _contextHelp.textHelp.connect(self.__displayHelp)
        # Need to pass event to original widget
        # type.mousePresseEvent() - How do I get type?

    @pyqtSlot(str)
    def __displayHelp(self, contextHelpText):
        self.setText(contextHelpText)

class ContextHelpButton(QPushButton, ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self, text):
        super(ContextHelpButton, self).__init__()
        self.setText(text)
        self.helpText = "This is QPushButton Context Help Text"

## It would be nice if I could use a Python Decorator, but
## don't know how yet.
## @ContextHelp
class ContextHelpComboBox(QComboBox, ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self):
        super(ContextHelpComboBox, self).__init__()
        self.helpText = "This is QComboBox Context Help Text"

class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUI()

    def setupUI(self):
        self.resize(250, 150)
        self.setWindowTitle('Context Help Example')
        self.show()

        button = ContextHelpButton("Test Button")

        comboBox = ContextHelpComboBox()
        comboBox.addItem("Test Item 1")
        comboBox.addItem("Test Item 2")
        comboBox.addItem("Test Item 3")

        helpTextDisplay = ContexHelpDisplay("Context Help")

        vBox = QVBoxLayout()
        vBox.addWidget(button)
        vBox.addWidget(comboBox)

        hBox = QHBoxLayout()
        hBox.addLayout(vBox)
        hBox.addWidget(helpTextDisplay)

        self.setLayout(hBox)

if __name__ == "__main__":
    app = QApplication([])
    ex = MainWindow()
    exit(app.exec_())

主要问题

如何将鼠标按下事件传递给原始小部件。

其他问题

  1. 这可以通过 Python 装饰器实现吗
  2. 是否有更好的设计模式更适合这种情况?

【问题讨论】:

  • 你想听到其他小部件的点击事件吗?为什么不使用 eventFilter?
  • 我正在尝试扩展任何小部件的 mousePressEvent 以在执行原始小部件 mousePressEvent 之外发出信号。
  • 这种设计不是 Qt 推荐的,如果你的任务是在你按下任何小部件时发出信号,你应该使用事件过滤器。

标签: python pyqt pyqt5


【解决方案1】:

@eyllanesc 所说的内容可能如下所示:

from PyQt5.QtWidgets import (QApplication, QLabel, QWidget, QPushButton, 
                             QComboBox, QHBoxLayout, QVBoxLayout)
from PyQt5.QtCore    import QObject, pyqtSignal, pyqtSlot,  QEvent, Qt


class ContextHelpSignals(QObject):
    """ Used to send signals globally through the application
        Eventually will be a Global Singelton                """
    textHelp = pyqtSignal(str)

    def __init__(self):
        super(ContextHelpSignals, self).__init__()


# Eventually will be a Global Singleton
_contextHelp = ContextHelpSignals()


class ContextHelpBaseClass(QObject):   
    """ All Widget that have context help inherits this class """
    def __init__(self, **kw):
        super(ContextHelpBaseClass, self).__init__()
        self.helpText = None

    def mousePressEvent(self, event):
        ''' THIS DISABLE WIDGETS NORMAL CLICK BEHAVIOR '''
        _contextHelp.textHelp.emit(self.helpText)
        # How can emit a signal and then pass this event to the normal widget
        print(type(super()))

    def SetHelpText(self, helpText):              # ???
        self.helpText = helpText


class ContexHelpDisplay(QLabel):
    """Dislpay Context Help frow widgets that have Context Help"""
    def __init__(self, text):
        super(ContexHelpDisplay, self).__init__()
        self.setText(text)
        _contextHelp.textHelp.connect(self.__displayHelp)
        # Need to pass event to original widget
        # type.mousePresseEvent() - How do I get type?

    @pyqtSlot(str)
    def __displayHelp(self, contextHelpText):
        self.setText(contextHelpText)

class ContextHelpButton(QPushButton, ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self, text):
        super(ContextHelpButton, self).__init__()
        self.setText(text)
        self.helpText = "This is <b>QPushButton</b> Context Help Text"


class ContextHelpComboBox(QComboBox):             # - --> , ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self):
        super(ContextHelpComboBox, self).__init__()
        self.helpText = "This is <b>QComboBox</b> Context Help Text"


class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUI()

    def setupUI(self):
        self.resize(250, 150)
        self.setWindowTitle('Context Help Example')
        self.show()

        button = ContextHelpButton("Test Button")

        self.comboBox = ContextHelpComboBox()     # + self.
        self.comboBox.addItem("Test Item 1")
        self.comboBox.addItem("Test Item 2")
        self.comboBox.addItem("Test Item 3")

        self.comboBox.installEventFilter(self)    # <- Installs an event filter filterObj on this object. 

        self.helpTextDisplay = ContexHelpDisplay("Context Help")

        vBox = QVBoxLayout()
        vBox.addWidget(button)
        vBox.addWidget(self.comboBox)

        hBox = QHBoxLayout()
        hBox.addLayout(vBox)
        hBox.addWidget(self.helpTextDisplay)
        self.setLayout(hBox)

    # Filters events if this object has been installed as an event filter for the watched object.
    def eventFilter(self, obj, event):
        if event.type() == QEvent.MouseButtonPress and obj is self.comboBox:
            self.helpTextDisplay.setText(self.comboBox.helpText)
        return super(MainWindow, self).eventFilter(obj, event)


if __name__ == "__main__":
    app = QApplication([])
    ex  = MainWindow()
    exit(app.exec_())

【讨论】:

  • 有没有installEventFilter(self)eventFilter()可以封装在基础ContextHelpBaseClass中?我将有数百个需要这个的小部件。
【解决方案2】:

作为后续,我设法通过将子类的引用存储在基类中,然后使用该引用传递事件来实现我想要的。

from PyQt5.QtWidgets import (QApplication, QLabel, QWidget, QPushButton,
                             QComboBox, QHBoxLayout, QVBoxLayout)
from PyQt5.QtCore    import QObject, pyqtSignal, pyqtSlot

class ContextHelpSignals(QObject):
    """ Used to send signals globally through the application
        Eventually will be a Global Singleton                """
    textHelp = pyqtSignal(str)

    def __init__(self):
        super(ContextHelpSignals, self).__init__()

# Eventually will be a Global Singleton
_contextHelp = ContextHelpSignals()

class ContexHelpDisplay(QLabel):
    """Display Context Help from widgets that have Context Help"""
    def __init__(self, text):
        super(ContexHelpDisplay, self).__init__()
        self.setText(text)
        _contextHelp.textHelp.connect(self.__displayHelp)

    @pyqtSlot(str)
    def __displayHelp(self, contextHelpText):
        self.setText(contextHelpText)

class ContextHelpBaseClass(QObject):
    """ All Widget that have context help inherits this class """
    def __init__(self, childObject):
        super(ContextHelpBaseClass, self).__init__()
        self.helpText = None
        self.childObject = childObject
        print( type(childObject) )

    def mousePressEvent(self, event):
        _contextHelp.textHelp.emit(self.helpText)
        # Pass mouse click event to native widget
        self.childObject.mousePressEvent(event)

class ContextHelpButton(QPushButton, ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self, text):
        super(ContextHelpButton, self).__init__(self)
        self.setText(text)
        self.helpText = "This is <b>QPushButton</b> Context Help Text"

    def test(self):
        print("BUTTON")

class ContextHelpComboBox(QComboBox, ContextHelpBaseClass):
    """QPush Button with Context Help"""
    def __init__(self):
        super(ContextHelpComboBox, self).__init__(self)
        self.helpText = "This is <b>QComboBox</b> Context Help Text"

    def test(self):
        print("Combo Box")

class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUI()

    def setupUI(self):
        self.resize(250, 150)
        self.setWindowTitle('Context Help Example')
        self.show()

        button = ContextHelpButton("Test Button")

        comboBox = ContextHelpComboBox()
        comboBox.addItem("Test Item 1")
        comboBox.addItem("Test Item 2")
        comboBox.addItem("Test Item 3")

        helpTextDisplay = ContexHelpDisplay("Context Help")

        button.clicked.connect(button.test)

        vBox = QVBoxLayout()
        vBox.addWidget(button)
        vBox.addWidget(comboBox)

        hBox = QHBoxLayout()
        hBox.addLayout(vBox)
        hBox.addWidget(helpTextDisplay)
        self.setLayout(hBox)

if __name__ == "__main__":
    app = QApplication([])
    ex  = MainWindow()
    exit(app.exec_())

【讨论】:

    【解决方案3】:

    另一个跟进。实现了上述相同的事情,但使用了 Python 装饰器。

    from PyQt5.QtWidgets import (QApplication, QLabel, QWidget, QPushButton,
                                 QComboBox, QHBoxLayout, QVBoxLayout)
    from PyQt5.QtCore    import QObject, pyqtSignal, pyqtSlot
    
    def ContextHelp(text):
        """Decorates a class by adding mousePressEvent method"""
        def AddMouseClickEventDecorator(_class):
            """ Adds 'mousePressEvent()' method to class"""
            def mousePressEvent(self, event):
                _contextHelp.textHelp.emit(text)       #Emit a signal to global ContextHelp Singleton
                super(_class, self).mousePressEvent(event) #Call Widget Base Class event
    
            #Add 'mousePressEvent' to _class
            setattr(_class, 'mousePressEvent', mousePressEvent)
            return _class #Decorated class
        return AddMouseClickEventDecorator
    
    
    
    class ContextHelpSignals(QObject):
        """ Used to send signals globally through the application
            Eventually will be a Global Singleton                """
        textHelp = pyqtSignal(str)
    
        def __init__(self):
            super(ContextHelpSignals, self).__init__()
    
    # Eventually will be a Global Singleton
    _contextHelp = ContextHelpSignals()
    
    class ContexHelpDisplay(QLabel):
        """Display Context Help from widgets that have Context Help"""
        def __init__(self, text):
            super(ContexHelpDisplay, self).__init__()
            self.setText(text)
            _contextHelp.textHelp.connect(self.__displayHelp)
    
        @pyqtSlot(str)
        def __displayHelp(self, contextHelpText):
            self.setText(contextHelpText)
    
    @ContextHelp("This is a push button")
    class ContextHelpButton(QPushButton):
        """QPush Button with Context Help"""
        def __init__(self, text):
            super(ContextHelpButton, self).__init__()
            self.setText(text)
    
    @ContextHelp("This is Combo Box")
    class ContextHelpComboBox(QComboBox):
        """QPush Button with Context Help"""
        def __init__(self):
            super(ContextHelpComboBox, self).__init__()
    
    
    class MainWindow(QWidget):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent=parent)
            self.setupUI()
    
        def setupUI(self):
            self.resize(250, 150)
            self.setWindowTitle('Context Help Example')
            self.show()
    
            button = ContextHelpButton("Test Button")
    
            comboBox = ContextHelpComboBox()
            comboBox.addItem("Test Item 1")
            comboBox.addItem("Test Item 2")
            comboBox.addItem("Test Item 3")
    
            helpTextDisplay = ContexHelpDisplay("Context Help")
    
            button.clicked.connect(self.TestButton)
    
            vBox = QVBoxLayout()
            vBox.addWidget(button)
            vBox.addWidget(comboBox)
    
            hBox = QHBoxLayout()
            hBox.addLayout(vBox)
            hBox.addWidget(helpTextDisplay)
            self.setLayout(hBox)
    
        def TestButton(self):
            print("Button Pressed.")
    
    if __name__ == "__main__":
        app = QApplication([])
        ex  = MainWindow()
        exit(app.exec_())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-02
      • 2019-06-14
      • 1970-01-01
      • 2022-08-09
      • 2018-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多