如何从线程内访问外部属性
How can I access an outside attribute from within a Thread
我用 PyQt5 Designer 设计了一个 GUI。
我希望 tryingMethod 始终检查来自服务器的传入消息,因此我创建了一个线程,以便 GUI 和 运行 方法可以 运行 同时有效。
问题是,当我尝试从 Ui_Form 访问变量 "Button" 时,它会抛出以下错误:AttributeError:'Ui_Form' 对象没有属性 'Button'。
我尝试将自己作为参数传递给线程,但它给了我 "Unexpected type(s): (() -> None, Ui_Form) Warning"
我已经尝试创建几个不同的 类 但我无法解决问题。任何帮助表示赞赏! :)
class Ui_Form(object):
def __init__(self):
t = threading.Thread(target=self.tryingMethod)
t.start()
def tryingMethod(self):
self.Button.setText("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
def setupUi(self, Form):
//Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
Qt 不允许直接从另一个线程更新 GUI,一个可能的解决方案是使用信号,但为此我们需要一个继承 QObject
的 class,另一方面它不建议修改 Qt Designer 生成的 class,class 仅用于填充小部件因此您可以利用创建从小部件继承的 class,并且由于它也是一个继承自 QObject
的小部件,因为 class 我们将创建一个信号,并将该信号连接到按钮的 setText()
方法。
class Ui_Form(object):
def setupUi(self, Form):
# Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
class Form(QtWidgets.QWidget, Ui_Form):
someSignal = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.setupUi(self)
t = threading.Thread(target=self.tryingMethod)
self.someSignal.connect(self.Button.setText)
t.start()
def tryingMethod(self):
self.someSignal.emit("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Form()
w.show()
sys.exit(app.exec_())
您在调用 ui.setupUi(Form)
之前实例化 ui = Ui_Form()
。
ui = Ui_Form()
接下来将调用 __init__
方法,该方法将开始创建并启动您的线程。这将(在最坏的情况下)尝试在创建按钮之前设置按钮,这可能是您收到错误的原因。
您需要先创建按钮,然后才能尝试使用它。一种选择是将线程创建移到 __init__
之外,如下所示:
class Ui_Form(object):
def startThread(self):
t = threading.Thread(target=self.tryingMethod)
t.start()
def tryingMethod(self):
self.Button.setText("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
def setupUi(self, Form):
# Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
# call after creation
ui.startThread()
sys.exit(app.exec_())
也就是说,您的代码还有一些其他错误,例如提到的 eyllanesc。
我用 PyQt5 Designer 设计了一个 GUI。
我希望 tryingMethod 始终检查来自服务器的传入消息,因此我创建了一个线程,以便 GUI 和 运行 方法可以 运行 同时有效。
问题是,当我尝试从 Ui_Form 访问变量 "Button" 时,它会抛出以下错误:AttributeError:'Ui_Form' 对象没有属性 'Button'。
我尝试将自己作为参数传递给线程,但它给了我 "Unexpected type(s): (() -> None, Ui_Form) Warning"
我已经尝试创建几个不同的 类 但我无法解决问题。任何帮助表示赞赏! :)
class Ui_Form(object):
def __init__(self):
t = threading.Thread(target=self.tryingMethod)
t.start()
def tryingMethod(self):
self.Button.setText("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
def setupUi(self, Form):
//Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
Qt 不允许直接从另一个线程更新 GUI,一个可能的解决方案是使用信号,但为此我们需要一个继承 QObject
的 class,另一方面它不建议修改 Qt Designer 生成的 class,class 仅用于填充小部件因此您可以利用创建从小部件继承的 class,并且由于它也是一个继承自 QObject
的小部件,因为 class 我们将创建一个信号,并将该信号连接到按钮的 setText()
方法。
class Ui_Form(object):
def setupUi(self, Form):
# Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
class Form(QtWidgets.QWidget, Ui_Form):
someSignal = QtCore.pyqtSignal(str)
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
self.setupUi(self)
t = threading.Thread(target=self.tryingMethod)
self.someSignal.connect(self.Button.setText)
t.start()
def tryingMethod(self):
self.someSignal.emit("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Form()
w.show()
sys.exit(app.exec_())
您在调用 ui.setupUi(Form)
之前实例化 ui = Ui_Form()
。
ui = Ui_Form()
接下来将调用 __init__
方法,该方法将开始创建并启动您的线程。这将(在最坏的情况下)尝试在创建按钮之前设置按钮,这可能是您收到错误的原因。
您需要先创建按钮,然后才能尝试使用它。一种选择是将线程创建移到 __init__
之外,如下所示:
class Ui_Form(object):
def startThread(self):
t = threading.Thread(target=self.tryingMethod)
t.start()
def tryingMethod(self):
self.Button.setText("TESTING") ##This doesn't work.
while True:
message = self.clientSocket.receive()
def setupUi(self, Form):
# Code has been shortened
self.Button.setFont(font)
self.Button.setCursor(QtGui.QCursor(QtCore.Qt.ForbiddenCursor))
self.Button.setObjectName("Button")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
# call after creation
ui.startThread()
sys.exit(app.exec_())
也就是说,您的代码还有一些其他错误,例如提到的 eyllanesc。