简体   繁体   English

自定义 class 中的信号和插槽,包括 PyQt QWidget 不工作

[英]The signal and slot within custom class including PyQt QWidget not working

I have some trouble customizing a class including a QPushButton and QLabel , and I just want to set the QPushButton checkable and define a slot to its toggled signal, in addition, the custom class inherents QObject .我在自定义包含QPushButtonQLabel的 class 时遇到了一些麻烦,我只想将QPushButton设置为可检查并为其切换信号定义一个插槽,此外,自定义 class 固有QObject

The code is shown below,代码如下所示,

import sys
from PyQt5 import QtGui
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import pyqtSlot, QObject


class CustomButton(QPushButton):
    def __init__(self, label='', parent=None):
        super().__init__(label, parent)
        self.setCheckable(True)
        self.toggled.connect(self.update)

    def update(self, state):
        if state:
            self.setStyleSheet('background-color: green')
        else:
            self.setStyleSheet('background-color: red')

class ButtonCtrl(QObject):
    def __init__(self,  parent=None, label=''):
        super().__init__()
        if isinstance(parent, QLayout):
            col = QVBoxLayout()
            parent.addLayout(col)
        else:
            col = QVBoxLayout(parent)
        self.text = ['ON', 'OFF']
        self.label = QLabel(label)
        self.button = QPushButton('ON')
        # self.button = CustomButton('ON')
        col.addWidget(self.label)
        col.addWidget(self.button)
        self.button.setCheckable(True)
        self.button.setChecked(True)
        self.button.toggled.connect(self.update)
        self.update(True)
        self.label.setFont(QFont('Microsoft YaHei', 14))
        self.button.setFont(QFont('Microsoft YaHei', 12, True))
        self.button.toggle()

    # @pyqtSlot(bool)
    def update(self, state):
        if state:
            self.button.setText(self.text[0])
            self.button.setStyleSheet('background-color: green')
        else:
            self.button.setText(self.text[-1])
            self.button.setStyleSheet('background-color: red')


class Window(QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        # set the layout
        layout = QVBoxLayout(self)
        but = ButtonCtrl(layout, "Test")
        #self.but = ButtonCtrl(layout, "Test")
        btn = CustomButton()
        layout.addWidget(btn)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    main = Window()
    main.show()
    sys.exit(app.exec_())

I have customized two buttons, named CustomButton(QPushButton) and ButtonCtrl(QObject) , and I have tested the slot in the main window, however the background update slot works for CustomButton(QPushbutton) and does not work for ButtonCtrl(QObject) , the slot function is not even invoked.我已经自定义了两个按钮,分别命名为CustomButton(QPushButton)ButtonCtrl(QObject) ,并且我已经测试了主 window 中的插槽,但是后台更新插槽适用于CustomButton(QPushbutton)而不适用于ButtonCtrl(QObject) ,槽 function 甚至没有被调用。

However, if I change the button member of ButtonCtrl(QObject) from QPushButton into my CustomButton(QPushButton) , the it will work well in the main window.但是,如果我将ButtonCtrl(QObject)的按钮成员从QPushButton更改为我的CustomButton(QPushButton) ,它将在主 window 中正常工作。 Furthermore, if the but in main window becomes a member of the main window class by setting self.but=ButtonCtrl(layout, "Test") , it will work as well.此外,如果主 window 中的but通过设置self.but=ButtonCtrl(layout, "Test")成为主 window class 的成员,它也可以正常工作。

I didn't find direct answer to it in Qt documentation which explains that我在 Qt 文档中没有找到直接答案,该文档解释了这一点

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner.当 state 的内部 state 以某种可能对对象的客户或所有者感兴趣的方式发生变化时,信号由 object 发出。 Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.信号是公共访问函数,可以从任何地方发出,但我们建议仅从定义信号及其子类的 class 发出它们。

I am not sure if the lifetime of the but causing this effect, hope to get an answer, thank you.我不确定是否会导致这种影响的寿命but希望得到答复,谢谢。

The problem is simple: The ButtonCtrl class object has a local scope so it will be destroyed, and why doesn't the same happen with the CustomButton class object? The problem is simple: The ButtonCtrl class object has a local scope so it will be destroyed, and why doesn't the same happen with the CustomButton class object? Well, because the ownership of a QWidget has its parent, and the parent of that button is the window, instead the ButtonCtrl object does not have it.好吧,因为 QWidget 的所有权有其父级,并且该按钮的父级是 window,而不是 ButtonCtrl object 没有它。 In this case the solution is to extend the life cycle of the variable, and in the case of a QObject there are several options:在这种情况下,解决方案是延长变量的生命周期,在 QObject 的情况下有几个选项:

  • make the class member variable,使 class 成员变量,
  • Place it in a container with a longer life cycle, or将其放入生命周期较长的容器中,或
  • establish a QObject parent with a longer life cycle.建立一个生命周期更长的 QObject 父级。

Using the third alternative is just to change to:使用第三种选择只是更改为:

class ButtonCtrl(QObject):
    def __init__(self,  parent=None, label=''):
        super().__init__(parent)
        # ...

The first option is the one commented on in your code:第一个选项是您的代码中注释的选项:

self.but = ButtonCtrl(layout, "Test")

and the second is similar:第二个类似:

self.container = list()
but = ButtonCtrl(layout, "Test")
self.container.append(but)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM