Python PyQt 信号并不总是有效
希望我的第一个问题能够正确遵循此处的指南。我正在尝试使用 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 信号未实现的功能?
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))
The self._view.mybutton.clicked.connect(self._printthis)
will no longer work.
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
造成此问题的原因是 PyCalcCtrl 对象未分配给变量,因此它将被销毁,因此“_printthis”方法将无法访问。另一方面,当使用 functools.partial 时,PyCalcCtrl 类的对象将被分配到该函数的范围,这就是它起作用的原因。
解决方案是将 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.
The solution is to assign the PyCalcCtrl object to a variable: