Python - Parents 和多线程问题
Python - Parents and Multithreading issues
我正在学习 PyQT 和多线程,我想我需要这里的帮助。在我的代码中,我有一个 class Tool
和一个线程 TaskThread
。从线程中,我调用函数 dummy_function
,它执行一个由 progress-bar.
在 GUI 中显示的过程
当 dummy_function
完成后,我想通过调用 self.outputText.setText("Hello")
在 GUI 中打印一条消息。但是,我收到以下错误:
AttributeError: 'TaskThread' object has no attribute 'outputText'
代码:
import sys
import datetime
import time
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Link to GUI
qtCreatorFile = "GUInterface.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
def time_converter_to_unix(start_datetime, end_datetime):
# Convert QTimeEdit to UNIX Timestamp (int, msec included), and then to float
start_datetime_unix_int = start_datetime.toMSecsSinceEpoch ()
start_datetime_unix = (float(start_datetime_unix_int) / 1000)
end_datetime_unix_int = end_datetime.toMSecsSinceEpoch ()
end_datetime_unix = (float(end_datetime_unix_int) / 1000)
return start_datetime_unix, end_datetime_unix
def dummy_function(self, start_datetime_unix, end_datetime_unix):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print OldValue
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print "NEW VALUE: ", NewValue
self.emit(QtCore.SIGNAL('CPU_VALUE'), NewValue)
self.outputText.setText("Hello")
class Tool(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
# Setting-ip UI
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Button Action
self.runButton.clicked.connect(self.onStart)
# Progress Bar and Label. At the begining, the bar is at 0
self.progressBar.setValue(0)
self.progressBar.setRange(0,100)
self.resultLabel.setText("Waiting...")
####################################
#TEST: Thread for progress bar
self.myLongTask = TaskThread()
self.connect(self.myLongTask, QtCore.SIGNAL('CPU_VALUE'), self.onProgress)
self.myLongTask.taskFinished.connect(self.onFinished)
####################################
def onStart(self):
self.progressBar.reset()
self.resultLabel.setText("In progress...")
print "(onStart)In progress mode executed"
print "(onStart)INITIALIZE THREAD"
self.myLongTask.start()
print "(onStart)THREAD EXECUTED"
self.myLongTask.start_dt = self.startTime.dateTime() # <----
self.myLongTask.end_dt = self.endTime.dateTime() # <----
def onProgress(self, i):
self.progressBar.setValue(i)
def onFinished(self):
# Stop the pulsation when the thread has finished
print "(onFinished) executed"
self.progressBar.setRange(0,1)
self.progressBar.setValue(1)
self.resultLabel.setText("Done")
class TaskThread(QtCore.QThread):
taskFinished = QtCore.pyqtSignal()
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# First, we read the times from the QDateTime elements in the interface
print "Getting times..."
start_datetime_unix, end_datetime_unix = time_converter_to_unix(self.start_dt, self.end_dt)
# Then, we put these values in my_function
print "Executing function..."
dummy_function(self, start_datetime_unix, end_datetime_unix)
# To finish, we execute onFinished.
print "Finishing thread..."
self.taskFinished.emit()
def main():
app = QtGui.QApplication(sys.argv)
window = Tool()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我尝试的 第一个 是将以下行添加到我的 onStart
函数中:
...
self.myLongTask.output_dt = self.outputText
...
然后,在 TaskThread
中,我将 dummy_function
的调用更改为:
...
dummy_function(self, start_datetime_unix, end_datetime_unix, self.output_dt)
...
最后,我通过添加这个新参数来更改我的 dummy_function:
def dummy_function(self, start_datetime_unix, end_datetime_unix, output_text):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print OldValue
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print "NEW VALUE: ", NewValue
self.emit(QtCore.SIGNAL('CPU_VALUE'), NewValue)
output_text.setText("Hello")
现在,当程序结束时,程序刚刚关闭并显示以下错误:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTextDocument(0xf13a60), parent's thread is QThread(0xc6f180), current thread is TaskThread(0xee0740)
为了试用程序,我上传了GUInterface,有需要的可以试试
你得到的第一个是因为 TaskThread
没有 outputText
作为属性,你的解决方案在某种程度上是正确的,因为它解决了第一个问题,但还有另一个问题,Qt 禁止直接从产生该问题的另一个线程更新 GUI。 Qt 指出,如果你想用其他线程的信息更新 GUI,你必须使用信号或 QMetaObject
,在这种情况下,我将通过创建一个名为 textChanged
的 signal
来解决它,这将连接到 outputText
的 setText()
方法。
另一方面,我看到您使用的是旧的连接方式:
self.connect(self.myLongTask, QtCore.SIGNAL('CPU_VALUE'), self.onProgress)
那个风格虽然还可以用,但是不推荐了,最好用new syntax.
最后连接到信号的函数称为槽,所以建议使用装饰器@QtCore.pyqtSlot(...)
,这种方法最大限度地减少资源消耗。
import sys
import datetime
import time
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Link to GUI
qtCreatorFile = "GUInterface.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
def time_converter_to_unix(start_datetime, end_datetime):
# Convert QTimeEdit to UNIX Timestamp (int, msec included), and then to float
start_datetime_unix_int = start_datetime.toMSecsSinceEpoch ()
start_datetime_unix = (float(start_datetime_unix_int) / 1000)
end_datetime_unix_int = end_datetime.toMSecsSinceEpoch ()
end_datetime_unix = (float(end_datetime_unix_int) / 1000)
return start_datetime_unix, end_datetime_unix
def dummy_function(self, start_datetime_unix, end_datetime_unix):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print(OldValue)
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print("NEW VALUE: ", NewValue)
self.cpuValueChanged.emit(NewValue)
self.textChanged.emit("Hello")
class Tool(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
# Setting-ip UI
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Button Action
self.runButton.clicked.connect(self.onStart)
# Progress Bar and Label. At the begining, the bar is at 0
self.progressBar.setValue(0)
self.progressBar.setRange(0,100)
self.resultLabel.setText("Waiting...")
####################################
#TEST: Thread for progress bar
self.myLongTask = TaskThread()
self.myLongTask.cpuValueChanged.connect(self.progressBar.setValue)
self.myLongTask.textChanged.connect(self.outputText.setText)
self.myLongTask.taskFinished.connect(self.onFinished)
####################################
@QtCore.pyqtSlot()
def onStart(self):
self.progressBar.reset()
self.resultLabel.setText("In progress...")
print("(onStart)In progress mode executed")
print("(onStart)INITIALIZE THREAD")
self.myLongTask.start()
print("(onStart)THREAD EXECUTED")
self.myLongTask.start_dt = self.startTime.dateTime() # <----
self.myLongTask.end_dt = self.endTime.dateTime() # <----
@QtCore.pyqtSlot()
def onFinished(self):
# Stop the pulsation when the thread has finished
print("(onFinished) executed")
self.progressBar.setRange(0,1)
self.progressBar.setValue(1)
self.resultLabel.setText("Done")
class TaskThread(QtCore.QThread):
cpuValueChanged = QtCore.pyqtSignal(int)
taskFinished = QtCore.pyqtSignal()
textChanged = QtCore.pyqtSignal(str)
def __del__(self):
self.wait()
def run(self):
# First, we read the times from the QDateTime elements in the interface
print("Getting times...")
start_datetime_unix, end_datetime_unix = time_converter_to_unix(self.start_dt, self.end_dt)
# Then, we put these values in my_function
print("Executing function...")
dummy_function(self, start_datetime_unix, end_datetime_unix)
# To finish, we execute onFinished.
print("Finishing thread...")
self.taskFinished.emit()
def main():
app = QtGui.QApplication(sys.argv)
window = Tool()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我正在学习 PyQT 和多线程,我想我需要这里的帮助。在我的代码中,我有一个 class Tool
和一个线程 TaskThread
。从线程中,我调用函数 dummy_function
,它执行一个由 progress-bar.
当 dummy_function
完成后,我想通过调用 self.outputText.setText("Hello")
在 GUI 中打印一条消息。但是,我收到以下错误:
AttributeError: 'TaskThread' object has no attribute 'outputText'
代码:
import sys
import datetime
import time
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Link to GUI
qtCreatorFile = "GUInterface.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
def time_converter_to_unix(start_datetime, end_datetime):
# Convert QTimeEdit to UNIX Timestamp (int, msec included), and then to float
start_datetime_unix_int = start_datetime.toMSecsSinceEpoch ()
start_datetime_unix = (float(start_datetime_unix_int) / 1000)
end_datetime_unix_int = end_datetime.toMSecsSinceEpoch ()
end_datetime_unix = (float(end_datetime_unix_int) / 1000)
return start_datetime_unix, end_datetime_unix
def dummy_function(self, start_datetime_unix, end_datetime_unix):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print OldValue
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print "NEW VALUE: ", NewValue
self.emit(QtCore.SIGNAL('CPU_VALUE'), NewValue)
self.outputText.setText("Hello")
class Tool(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
# Setting-ip UI
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Button Action
self.runButton.clicked.connect(self.onStart)
# Progress Bar and Label. At the begining, the bar is at 0
self.progressBar.setValue(0)
self.progressBar.setRange(0,100)
self.resultLabel.setText("Waiting...")
####################################
#TEST: Thread for progress bar
self.myLongTask = TaskThread()
self.connect(self.myLongTask, QtCore.SIGNAL('CPU_VALUE'), self.onProgress)
self.myLongTask.taskFinished.connect(self.onFinished)
####################################
def onStart(self):
self.progressBar.reset()
self.resultLabel.setText("In progress...")
print "(onStart)In progress mode executed"
print "(onStart)INITIALIZE THREAD"
self.myLongTask.start()
print "(onStart)THREAD EXECUTED"
self.myLongTask.start_dt = self.startTime.dateTime() # <----
self.myLongTask.end_dt = self.endTime.dateTime() # <----
def onProgress(self, i):
self.progressBar.setValue(i)
def onFinished(self):
# Stop the pulsation when the thread has finished
print "(onFinished) executed"
self.progressBar.setRange(0,1)
self.progressBar.setValue(1)
self.resultLabel.setText("Done")
class TaskThread(QtCore.QThread):
taskFinished = QtCore.pyqtSignal()
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
# First, we read the times from the QDateTime elements in the interface
print "Getting times..."
start_datetime_unix, end_datetime_unix = time_converter_to_unix(self.start_dt, self.end_dt)
# Then, we put these values in my_function
print "Executing function..."
dummy_function(self, start_datetime_unix, end_datetime_unix)
# To finish, we execute onFinished.
print "Finishing thread..."
self.taskFinished.emit()
def main():
app = QtGui.QApplication(sys.argv)
window = Tool()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我尝试的 第一个 是将以下行添加到我的 onStart
函数中:
...
self.myLongTask.output_dt = self.outputText
...
然后,在 TaskThread
中,我将 dummy_function
的调用更改为:
...
dummy_function(self, start_datetime_unix, end_datetime_unix, self.output_dt)
...
最后,我通过添加这个新参数来更改我的 dummy_function:
def dummy_function(self, start_datetime_unix, end_datetime_unix, output_text):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print OldValue
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print "NEW VALUE: ", NewValue
self.emit(QtCore.SIGNAL('CPU_VALUE'), NewValue)
output_text.setText("Hello")
现在,当程序结束时,程序刚刚关闭并显示以下错误:
QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0xf13a60), parent's thread is QThread(0xc6f180), current thread is TaskThread(0xee0740)
为了试用程序,我上传了GUInterface,有需要的可以试试
你得到的第一个是因为 TaskThread
没有 outputText
作为属性,你的解决方案在某种程度上是正确的,因为它解决了第一个问题,但还有另一个问题,Qt 禁止直接从产生该问题的另一个线程更新 GUI。 Qt 指出,如果你想用其他线程的信息更新 GUI,你必须使用信号或 QMetaObject
,在这种情况下,我将通过创建一个名为 textChanged
的 signal
来解决它,这将连接到 outputText
的 setText()
方法。
另一方面,我看到您使用的是旧的连接方式:
self.connect(self.myLongTask, QtCore.SIGNAL('CPU_VALUE'), self.onProgress)
那个风格虽然还可以用,但是不推荐了,最好用new syntax.
最后连接到信号的函数称为槽,所以建议使用装饰器@QtCore.pyqtSlot(...)
,这种方法最大限度地减少资源消耗。
import sys
import datetime
import time
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Link to GUI
qtCreatorFile = "GUInterface.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
def time_converter_to_unix(start_datetime, end_datetime):
# Convert QTimeEdit to UNIX Timestamp (int, msec included), and then to float
start_datetime_unix_int = start_datetime.toMSecsSinceEpoch ()
start_datetime_unix = (float(start_datetime_unix_int) / 1000)
end_datetime_unix_int = end_datetime.toMSecsSinceEpoch ()
end_datetime_unix = (float(end_datetime_unix_int) / 1000)
return start_datetime_unix, end_datetime_unix
def dummy_function(self, start_datetime_unix, end_datetime_unix):
# Dummy function, just to simulate a task. It takes parameters just for testing.
result = start_datetime_unix * end_datetime_unix
# Pre-steps for mapping from one range to other (progress-bar)
OldMax = 99999
OldMin = 1
NewMax = 100
NewMin = 1
OldRange = (OldMax - OldMin)
NewRange = (NewMax - NewMin)
u = 1
for i in range (OldMax):
u = i*2
OldValue = i
print(OldValue)
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
print("NEW VALUE: ", NewValue)
self.cpuValueChanged.emit(NewValue)
self.textChanged.emit("Hello")
class Tool(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent = None):
# Setting-ip UI
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
# Button Action
self.runButton.clicked.connect(self.onStart)
# Progress Bar and Label. At the begining, the bar is at 0
self.progressBar.setValue(0)
self.progressBar.setRange(0,100)
self.resultLabel.setText("Waiting...")
####################################
#TEST: Thread for progress bar
self.myLongTask = TaskThread()
self.myLongTask.cpuValueChanged.connect(self.progressBar.setValue)
self.myLongTask.textChanged.connect(self.outputText.setText)
self.myLongTask.taskFinished.connect(self.onFinished)
####################################
@QtCore.pyqtSlot()
def onStart(self):
self.progressBar.reset()
self.resultLabel.setText("In progress...")
print("(onStart)In progress mode executed")
print("(onStart)INITIALIZE THREAD")
self.myLongTask.start()
print("(onStart)THREAD EXECUTED")
self.myLongTask.start_dt = self.startTime.dateTime() # <----
self.myLongTask.end_dt = self.endTime.dateTime() # <----
@QtCore.pyqtSlot()
def onFinished(self):
# Stop the pulsation when the thread has finished
print("(onFinished) executed")
self.progressBar.setRange(0,1)
self.progressBar.setValue(1)
self.resultLabel.setText("Done")
class TaskThread(QtCore.QThread):
cpuValueChanged = QtCore.pyqtSignal(int)
taskFinished = QtCore.pyqtSignal()
textChanged = QtCore.pyqtSignal(str)
def __del__(self):
self.wait()
def run(self):
# First, we read the times from the QDateTime elements in the interface
print("Getting times...")
start_datetime_unix, end_datetime_unix = time_converter_to_unix(self.start_dt, self.end_dt)
# Then, we put these values in my_function
print("Executing function...")
dummy_function(self, start_datetime_unix, end_datetime_unix)
# To finish, we execute onFinished.
print("Finishing thread...")
self.taskFinished.emit()
def main():
app = QtGui.QApplication(sys.argv)
window = Tool()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()