目前正在编写一个GUI应用程序,该应用程序是从Foundry's Nuke启动的,并与Foundry‘s Nuke接口,这是一个视觉效果合成软件(尽管这不是AFAIK问题,因为它在IDE中是可重复的)。我试图弄清楚为什么在我的Pyside2 GUI中运行一个本机Python线程在调用导致更新QLabel的函数时会崩溃,而另一个调用相同函数来更新不同QLabel的类似函数似乎每次都正常工作。这两者都是线程化的,因为它们是涉及数据I/O和API请求的后台进程,可能需要一些时间。这是Pyside2 2/QT5.12和python3.7中的内容,由于软件的限制,这两个版本都不能更新。
我已经读过这里,使用QThread通常“比常规的线程模块更好”,用于与Qt交互的这里,以及“在QLineEdit上调用setText()之类的PyQt GUI调用是不允许在线程中使用的”。如果这是解决方案,我可能会切换到QThread,但是在创建单独的工作类之后,我需要进行更多的数据传递,正如我所见过的许多教程以及这就是答案所建议的那样,所以如果可能的话,我想让它继续使用本机线程模块,因为这看起来不那么复杂。
但是,问一问似乎是个好主意,因为连接到线程的一个按钮似乎能够很好地更新QLabel,而另一个按钮总是崩溃,这是没有道理的。下面是一些复制问题的简化代码。connectAPI函数用于连接到外部API,该API为当前镜头执行一些逻辑和处理,使用time.sleep()进行模拟。(我还知道我的函数和变量与PEP8不兼容,camelCase是这个代码库中的惯例)
# PySide2 / QT 5.12
# Python 3.7
import PySide2.QtCore as QtCore
import PySide2.QtGui as QtGui
import PySide2.QtWidgets as QtWidgets
import time
import sys
import threading
class MyMainWindow(QtWidgets.QWidget):
def __init__(self, pathToFile):
super(MyMainWindow, self).__init__()
self.resize(250, 200)
self.connectionEstablished = False
self.inputFile = pathToFile
self.createWidgets()
self.layoutWidgets()
def startConnectionThread(self):
self.connectionValid = False
# This never shows in the UI, but does print from the updateStatus(), so presumably it's just immediately getting changed by the
# first updateStatus() call inside of connectAPI
self.updateStatus(self.connectionLabel, 'inprogress', 'Attempting Connection')
connectionThread = threading.Thread(target=self.connectAPI)
connectionThread.start()
def connectAPI(self):
print("connectAPI function")
# This works fine, even though it "shouldn't" because it's updating a QLabel from inside a thread
self.updateStatus(self.connectionLabel, 'inprogress', "Searching...")
# Simulated external API connection and processing
self.connectionObject = True
for x in range(0,11):
print(x)
time.sleep(0.2)
if self.connectionObject == False:
self.updateStatus(self.connectionLabel, 'failure', "Object Not Found!")
return False
else:
self.objectFound = True
# This works fine, even though it "shouldn't" because it's updating a QLabel from inside a thread
self.updateStatus(self.connectionLabel, 'success', "Search Complete")
def updateStatus(self, label, color, text):
print(f"update status: {text}")
if color == 'success':
color = 'ForestGreen'
if color == 'failure':
color = 'OrangeRed'
elif color == 'inprogress':
color = 'Tan'
label.setText('<FONT COLOR="{color}"><b>{text}<b></FONT>'.format(color=color, text=text))
def submitButtonThread(self):
print("starting submit thread")
submit_thread = threading.Thread(target=self.testFunction)
submit_thread.start()
def testFunction(self):
print("test function")
for x in range(0, 11):
print(x)
time.sleep(0.2)
# Uncommenting the following line will crash the GUI, but only in this function
self.updateStatus(self.submitLabel, 'inprogress', 'test function end...')
def createWidgets(self):
# Widget - Verify Connection Button
self.connectionGet = QtWidgets.QPushButton('Verify Name')
self.connectionGet.clicked.connect(self.startConnectionThread)
# Widget - Connection Success Label
self.connectionLabel = QtWidgets.QLabel('')
self.connectionLabel.sizeHint()
self.connectionLabel.setAlignment(QtCore.Qt.AlignCenter)
# Widget - Creation Button
self.createSubmitButton = QtWidgets.QPushButton('Create Submission')
self.createSubmitButton.clicked.connect(self.submitButtonThread)
# Widget - Submit Success Label
self.submitLabel = QtWidgets.QLabel('')
self.submitLabel.sizeHint()
self.submitLabel.setAlignment(QtCore.Qt.AlignCenter)
self.submitLabel.setMaximumHeight(30)
def layoutWidgets(self):
self.mainLayout = QtWidgets.QVBoxLayout(self)
self.connectionGroup = QtWidgets.QGroupBox('API Connection')
connectionLayout = QtWidgets.QGridLayout()
connectionLayout.addWidget(self.connectionGet, 0, 2, 1, 2)
connectionLayout.addWidget(self.connectionLabel, 1, 0, 1, 4)
self.connectionGroup.setLayout(connectionLayout)
self.creationSection = QtWidgets.QGroupBox('Creation Section')
creationLayout = QtWidgets.QGridLayout()
creationLayout.addWidget(self.createSubmitButton, 0, 2, 1, 2)
creationLayout.addWidget(self.submitLabel, 1, 0, 1, 4)
self.creationSection.setLayout(creationLayout)
# Add them all to main
self.mainLayout.addWidget(self.connectionGroup)
self.mainLayout.addSpacing(10)
self.mainLayout.addWidget(self.creationSection)
self.setLayout(self.mainLayout)
# This function is how it's called from inside Nuke, included for completeness
def launchGUI(pathToFile):
global window
window = MyMainWindow(pathToFile)
window.show()
# This is used inside the IDE for testing
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
global window
window = MyMainWindow("//Server/path/to/file.txt")
window.show()
sys.exit(app.exec_())单击connectionGet按钮时输出:
update status: Attempting Connection
connectAPI function
update status: Searching...
0
1
2
3
4
5
6
7
8
9
10
update status: Search Complete单击createSubmitButton按钮时输出:
starting submit thread
test function
0
1
2
3
4
5
6
7
8
9
10
update status: test function end...
Process finished with exit code -1073741819 (0xC0000005)(它与退出代码一起崩溃)
我试着交换这两个函数所针对的线程,看看结果是否相同,我认为可能只有一组线程/更新会发生,并且得到了更令人困惑的结果:
connectionThread = threading.Thread(target=self.testFunction)在以下方面的成果:
update status: Attempting Connection
test function
0
1
2
3
4
5
6
7
8
9
10
update status: test function end...
Process finished with exit code -1073741819 (0xC0000005)(坠机)
submit_thread = threading.Thread(target=self.connectAPI)在以下方面的成果:
starting submit thread
connectAPI function
update status: Searching...
0
1
Process finished with exit code -1073741819 (0xC0000005)(坠机)
发布于 2022-10-28 17:33:25
从任何线程(主线程除外)对python中的QT进行任何更新,这很可能会导致分段错误,但在某些情况下,它可能不会在这一行上进行,它可能会在完全不同的行上执行,您可能会在头上挠几天,为什么它会在这里崩溃而不是在那里崩溃呢?尝试在不同的操作系统上,它会崩溃,但不是在这里,等等。
将GUI的所有更新保存在主线程中,并使用从子线程发出的信号在主线程上运行函数,这些函数将在线程函数完成执行后更新GUI,您可以在这个问题中找到一个示例,这个问题是这个问题的重复。更新MultiThreaded PyQT中的图形用户界面元素
https://stackoverflow.com/questions/74238920
复制相似问题