首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么使用QT5.12线程将在一个函数中成功地更新QLabel,而在另一个类似函数中却会崩溃?

为什么使用QT5.12线程将在一个函数中成功地更新QLabel,而在另一个类似函数中却会崩溃?
EN

Stack Overflow用户
提问于 2022-10-28 17:29:03
回答 1查看 34关注 0票数 0

目前正在编写一个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是这个代码库中的惯例)

代码语言:javascript
复制
# 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按钮时输出:

代码语言:javascript
复制
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按钮时输出:

代码语言:javascript
复制
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)

(它与退出代码一起崩溃)

我试着交换这两个函数所针对的线程,看看结果是否相同,我认为可能只有一组线程/更新会发生,并且得到了更令人困惑的结果:

代码语言:javascript
复制
connectionThread = threading.Thread(target=self.testFunction)

在以下方面的成果:

代码语言:javascript
复制
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)

(坠机)

代码语言:javascript
复制
submit_thread = threading.Thread(target=self.connectAPI)

在以下方面的成果:

代码语言:javascript
复制
starting submit thread 
connectAPI function 
update status: Searching... 
0 
1

Process finished with exit code -1073741819 (0xC0000005)

(坠机)

EN

回答 1

Stack Overflow用户

发布于 2022-10-28 17:33:25

从任何线程(主线程除外)对python中的QT进行任何更新,这很可能会导致分段错误,但在某些情况下,它可能不会在这一行上进行,它可能会在完全不同的行上执行,您可能会在头上挠几天,为什么它会在这里崩溃而不是在那里崩溃呢?尝试在不同的操作系统上,它会崩溃,但不是在这里,等等。

将GUI的所有更新保存在主线程中,并使用从子线程发出的信号在主线程上运行函数,这些函数将在线程函数完成执行后更新GUI,您可以在这个问题中找到一个示例,这个问题是这个问题的重复。更新MultiThreaded PyQT中的图形用户界面元素

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74238920

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档