Python PyQt 信号并不总是有效 [英] Python PyQt signals are not always working

查看:82
本文介绍了Python PyQt 信号并不总是有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Hopefully I am following the guidelines correctly here with my first question. I am trying to create a GUI with the MVC structure. I am having difficulty with understanding why my signals are not always being picked up by the controller. I know that there is just something simple that I'm missing. I'm attaching code from a simple calculator which I used as a guide. I removed most of the features to simplify this as much as possible. It is now only 3 of the original buttons and my own button. For debugging, I just have the value on the button printed out when you press it.

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()

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

This set of code works, BUT if I comment out the

        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 信号没有做的事情?

What is the btn.clicked.connect(partial(self._printthat, btnText)) line doing which is allowing any other signal I put in def _connectSignals(self): to work. What aspect of that line is achieving something that the mybutton signal isn't doing?

推荐答案

这个问题是因为 PyCalcCtrl 对象没有被赋值给一个变量,所以它会被销毁,因此_printthis"被破坏了.方法将无法访问.另一方面,当使用 functools.partial 时,PyCalcCtrl 类的对象被分配给该函数的作用域,这就是它起作用的原因.

The problem is caused because the PyCalcCtrl object is not assigned to a variable so it will be destroyed and therefore the "_printthis" method will not be accessible. On the other hand, when functools.partial is used then the object of the PyCalcCtrl class is assigned to the scope of that function, that's why it works.

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

The solution is to assign the PyCalcCtrl object to a variable:

ctrl = PyCalcCtrl(model=model, view=view)

这篇关于Python PyQt 信号并不总是有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆