Python PyQt 信号并不总是有效

Posted

技术标签:

【中文标题】Python PyQt 信号并不总是有效【英文标题】:Python PyQt signals are not always working 【发布时间】:2020-11-19 17:23:10 【问题描述】:

希望我在第一个问题上正确地遵循了指南。我正在尝试使用 MVC 结构创建一个 GUI。我很难理解为什么控制器并不总是接收到我的信号。我知道我缺少一些简单的东西。我附上了一个简单计算器的代码,我用作指南。我删除了大部分功能以尽可能简化这一点。它现在只有 3 个原始按钮和我自己的按钮。为了调试,我只是在你按下按钮时打印出按钮上的值。

import sys

# Import QApplication and the required widgets from PyQt5.QtWidgets
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QMainWindow
from PySide2.QtWidgets import QWidget
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QGridLayout
from PySide2.QtWidgets import QLineEdit
from PySide2.QtWidgets import QPushButton
from PySide2.QtWidgets import QVBoxLayout
from functools import partial

ERROR_MSG = 'ERROR'

# Create a subclass of QMainWindow to setup the calculator's GUI
class PyCalcUi(QMainWindow):
    """PyCalc's View (GUI)."""
    def __init__(self):
        """View initializer."""
        super().__init__()
        # Set some main window's properties
        self.setWindowTitle('PyCalc')
        self.setFixedSize(235, 235)
        # Set the central widget and the general layout
        self.generalLayout = QVBoxLayout()
        self._centralWidget = QWidget(self)
        self.setCentralWidget(self._centralWidget)
        self._centralWidget.setLayout(self.generalLayout)
        # Create the display and the buttons
        self._createDisplay()
        self._createButtons()

    def _createDisplay(self):
        """Create the display."""
        # Create the display widget
        self.display = QLineEdit()
        # Set some display's properties
        self.display.setFixedHeight(35)
        self.display.setAlignment(Qt.AlignRight)
        self.display.setReadOnly(True)
        # Add the display to the general layout
        self.generalLayout.addWidget(self.display)
        
    def _createButtons(self):
        """Create the buttons."""
        self.buttons = 
        buttonsLayout = QGridLayout()
        # Button text | position on the QGridLayout
        buttons = '7': (0, 0),
                   '8': (0, 1),
                   '9': (0, 2),
                  
        # Create the buttons and add them to the grid layout
        for btnText, pos in buttons.items():
            self.buttons[btnText] = QPushButton(btnText)
            self.buttons[btnText].setFixedSize(40, 40)
            buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1])
        self.mybutton = QPushButton("5")
        buttonsLayout.addWidget(self.mybutton,1,0)
        # Add buttonsLayout to the general layout
        self.generalLayout.addLayout(buttonsLayout)       
   
# Create a Controller class to connect the GUI and the model
class PyCalcCtrl:
    """PyCalc Controller class."""
    def __init__(self, model, view):
        """Controller initializer."""
        self._evaluate = model
        self._view = view
        # Connect signals and slots
        self._connectSignals()
        
    def _printthis(self):
        print("Hi")
        
    def _printthat(self, buttonvalue):
        print(buttonvalue)
        
    def _connectSignals(self):
        """Connect signals and slots."""
        self._view.mybutton.clicked.connect(self._printthis)
        for btnText, btn in self._view.buttons.items():
            btn.clicked.connect(partial(self._printthat, btnText))


# Create a Model to handle the calculator's operation
def evaluateExpression(expression):
    """Evaluate an expression."""
    try:
        result = str(eval(expression, , ))
    except Exception:
        result = ERROR_MSG

    return result

        
# Client code
def main():
    """Main function."""
    # Create an instance of QApplication if it doesn't exist
    pycalc = QApplication.instance()
    if pycalc is None: 
        pycalc = QApplication(sys.argv)
    # Show the calculator's GUI
    view = PyCalcUi()
    view.show()
    # Create instances of the model and the controller
    model = evaluateExpression
    PyCalcCtrl(model=model, view=view)
    # Execute the calculator's main loop
    sys.exit(pycalc.exec_())

if __name__ == '__main__':
    main()

这组代码有效,但是如果我注释掉

        for btnText, btn in self._view.buttons.items():
            btn.clicked.connect(partial(self._printthat, btnText))

self._view.mybutton.clicked.connect(self._printthis) 将不再工作。

btn.clicked.connect(partial(self._printthat, btnText)) 行是做什么的,它允许我在 def _connectSignals(self): 中输入的任何其他信号工作。该行的哪个方面实现了 mybutton 信号没有做的事情?

【问题讨论】:

【参考方案1】:

问题是因为 PyCalcCtrl 对象没有分配给变量,所以它会被销毁,因此“_printthis”方法将无法访问。另一方面,当使用 functools.partial 时,会将 PyCalcCtrl 类的对象分配给该函数的范围,这就是它起作用的原因。

解决方法是将 PyCalcCtrl 对象赋值给一个变量:

<b>ctrl =</b> PyCalcCtrl(model=model, view=view)

【讨论】:

谢谢!我现在明白了。

以上是关于Python PyQt 信号并不总是有效的主要内容,如果未能解决你的问题,请参考以下文章

Python PyQt 信号并不总是有效

为啥 QSplashscreen 并不总是有效?

Scrapy + pyqt5:信号仅在主线程错误中有效

python pyqt5 自定义信号和槽

当我专门双击左键时,Pyqt5 崩溃而没有错误(双右键有效)

使用 pyqt4 创建类信号