从 PyQt5 迁移到 Pyside2 后调用 QFileDialog 时,QThread 关闭 [英] QThread closes whenever QFileDialog Called After Migrating from PyQt5 to Pyside2

查看:104
本文介绍了从 PyQt5 迁移到 Pyside2 后调用 QFileDialog 时,QThread 关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我目前正在将我的源代码从 PyQt5 迁移到 PySide2,这需要我更改一些语法.正如这个 网站 所说,它只需要 3 件事来做迁移从 PyQt 到 Pyside2.

First of all, I'm currently migrating my source code from PyQt5 to PySide2 which requires me to change some of the syntaxes. As this site said that it only needs 3 things to do migrate from PyQt to Pyside2.

1.app.exec_.exec_ 被用作 exec 是一个 Python2 关键字.在 Python3 下,PyQt5 允许使用 exec 而不允许使用 PySide2.

1.app.exec_. exec_ was used as exec is a Python2 keyword. Under Python3, PyQt5 allows the use of exec but not PySide2.

2.在 PyQt5 下是 QtCore.pyqtSignal 和 QtCore.pyqtSlot,在 PySide2 下是 QtCore.Signal 和 QtCore.Slot.

2.Under PyQt5 it’s QtCore.pyqtSignal and QtCore.pyqtSlot and under PySide2 it’s QtCore.Signal and QtCore.Slot .

3.加载 Ui 文件.

3.loading Ui files.

但无论如何后来当我尝试运行我的代码时,它给了我以下错误:

But anyway later on when I tried to run my code it gave me this following error:

QThread:线程仍在运行时被销毁

QThread: Destroyed while thread is still running

我有 2000 多行代码,除了我上次尝试调用 QFileDialog 应该没有问题的操作之外,我无法确定哪个是导致此问题的原因(我已经用 PyQt 对此进行了测试导入,没有问题,也没有警告).但在 PySide2 中,这绝对可能是它的原因.我查看了这个,他没有有和我完全一样的问题.我不想从不同的线程调用 QFileDialog.

I had more than 2000 lines of code and I cannot determine which is the cause of this other than my last action which is trying to call QFileDialog which shouldn't be a problem (I've tested this with PyQt import and there's no problem and no warning at all). But in PySide2 it definitely might be the cause of it. I look up into this, he doesn't have the same problem as mine exactly. I'm not trying to call QFileDialog from different thread.

这是我在 PyQt5 中工作代码的最小可重现示例:

this is the minimal reproducible example of my working code in PyQt5:

import sys
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QWidget, QDialog
import random

class MyWidget(QtWidgets.QWidget):

    def __init__(self):

        QtWidgets.QWidget.__init__(self)

        self.path = os.path.abspath(os.path.dirname(sys.argv[0]))
        self.button = QtWidgets.QPushButton("Open File")
        self.labelFile = QtWidgets.QLabel("empty")
        self.labelData = QtWidgets.QLabel("None")
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.labelFile)
        self.layout.addWidget(self.labelData)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.open_file)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update_data_value)
        timer.start(1000)

    def open_file(self):
        x = QFileDialog.getOpenFileName(self,"Pilih File CSV yang Ingin Diproses",self.path,"CSV Files (*.csv)")
        self.labelFile.setText(x[0])

    def update_data_value(self):
        self.DataProcess = DataProcess()
        self.DataProcess.progress.connect(self.update_data_label)
        self.DataProcess.start()

    def update_data_label(self,x):
        self.labelData.setText(str(x[0]))

class DataProcess(QtCore.QThread):
    progress = QtCore.pyqtSignal(object)
    def __init__(self):
        QtCore.QThread.__init__(self)    
    
    def run(self):
        x = random.randint(1,100)
        self.progress.emit([str(x)+ " from thread"])

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())

这是在将导入相应地重命名为 PySide2 并将pyqtsignal"重命名为Signal"之后 PySide2 中的非工作方法

and this is the non-working one in PySide2 after renaming import accordingly to PySide2 also renaming 'pyqtsignal' to 'Signal'

import sys
import os
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QWidget, QDialog
import random

class MyWidget(QtWidgets.QWidget):

    def __init__(self):

        QtWidgets.QWidget.__init__(self)

        self.path = os.path.abspath(os.path.dirname(sys.argv[0]))
        self.button = QtWidgets.QPushButton("Open File")
        self.labelFile = QtWidgets.QLabel("empty")
        self.labelData = QtWidgets.QLabel("None")
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.labelFile)
        self.layout.addWidget(self.labelData)
        self.setLayout(self.layout)
        self.button.clicked.connect(self.open_file)
        timer = QtCore.QTimer(self)
        timer.timeout.connect(self.update_data_value)
        timer.start(1000)

    def open_file(self):
        x = QFileDialog.getOpenFileName(self,"Pilih File CSV yang Ingin Diproses",self.path,"CSV Files (*.csv)")
        self.labelFile.setText(x[0])

    def update_data_value(self):
        self.DataProcess = DataProcess()
        self.DataProcess.progress.connect(self.update_data_label)
        self.DataProcess.start()

    def update_data_label(self,x):
        self.labelData.setText(str(x[0]))

class DataProcess(QtCore.QThread):
    progress = QtCore.Signal(object)
    def __init__(self):
        QtCore.QThread.__init__(self)    
    
    def run(self):
        x = random.randint(1,100)
        self.progress.emit([str(x)+ " from thread"])

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())

所以在创建这个最小的例子之后,我意识到 PySide QFileDialog 使 QThread 停止,而 PyQt QFileDialog 不会冻结主线程.在类似的语法架构中,我可以做些什么来处理这个问题?(例如,不使用movetothread"或QObject")

so after creating this minimal example, I realized that PySide QFileDialog makes the QThread stop while PyQt QFileDialog doesn't freeze the main thread. Is there anything I could do to handle this in similar syntax architecture? (e.g not using "movetothread" or "QObject")

推荐答案

问题是每次创建新线程时都覆盖self.DataProcess,这可能会导致之前的对象在 Qt 有机会删除它之前被 Python 垃圾收集.如果 Qt 尝试删除不再存在的对象,这可能会导致核心转储.这类问题在 PyQt 和 PySide 中都很常见,通常是由于没有保持对依赖对象的正确引用造成的.正常的解决方案是确保为受影响的对象指定一个父对象,并在必要时在适当的时候明确删除它们.

The problem is that you're overwriting self.DataProcess every time a new thread is created, which may cause the previous object to be garbage-collected by Python before Qt has a chance to delete it. This can result in a core-dump if Qt tries to delete an object which is no longer there. Problems of this kind are quite common in both PyQt and PySide, and are usually caused by not keeping proper references to dependant objects. The normal solution is to ensure that the affected objects are given a parent and, if necessary, explicitly delete them at an appropriate time.

这是修复示例的一种方法:

Here is one way to fix your example:

class MyWidget(QtWidgets.QWidget):
    ...

    def update_data_value(self):
        # ensure the thread object has a parent
        process = DataProcess(self)
        process.progress.connect(self.update_data_label)
        process.start()

    def update_data_label(self,x):
        self.labelData.setText(str(x[0]))

class DataProcess(QtCore.QThread):
    progress = QtCore.Signal(object)
    def __init__(self, parent):
        # ensure the thread object has a parent
        QtCore.QThread.__init__(self, parent)

    def run(self):
        x = random.randint(1,100)
        self.progress.emit([str(x)+ " from thread"])
        # explicitly schedule for deletion
        self.deleteLater()

在这种特殊情况下,很难确切说明为什么 PySide 的行为与 PyQt 不同.它通常归结为两种实现之间的低级差异.可能存在影响 PyQt 但不影响 PySide 的等效情况.但是,如果您仔细管理对象引用和清理,通常可以消除这种差异.

It's hard to say exactly why PySide behaves differently to PyQt in this particular case. It usually just comes down to low-level differences between the two implementations. Probably there are equivalent cases that affect PyQt but not PySide. However, if you manage object references and cleanup carefully, such differences can usually be eliminated.

这篇关于从 PyQt5 迁移到 Pyside2 后调用 QFileDialog 时,QThread 关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆