Przede wszystkim migruję mój kod źródłowy z Pyqt5 do PYSIDE2, który wymaga zmiany niektórych składni. Jak ta Witryna powiedział, że potrzebuje tylko 3 rzeczy do zriżenia Z Pyqt do PYSIDE2.

1.Pap.exec_. Exec_ został użyty jako EXEC to słowo kluczowe Python2. W Python3 pyqt5 umożliwia użycie EXEC, ale nie pyside2.

2. Piqt5 To Qtcore.pyqtignal i QTCore.pyqtslot i pod PYSIDE2 to QTCORE.Signal i Qtcore.sot.

3. Ładowanie plików UI.

Ale w każdym razie później, kiedy próbowałem uruchomić mój kod, dał mi ten następujący błąd:

QTHREAD: Zniszczony, gdy wątek wciąż działa

Miałem ponad 2000 linii kodu i nie mogę określić, co jest przyczyną tego innego niż moja ostatnia akcja, która próbuje zadzwonić do QFiledIalog, który nie powinien być problemem ( przetestowałem to z importem Pyqt i jest Nie ma problemu i bez ostrzeżenia w ogóle ). Ale w PYSIDE2 zdecydowanie może być przyczyną tego. Patrzę na To, prawda mieć taki sam problem jak najlepiej. Nie próbuję zadzwonić QFiledialog z innego wątku.

Jest to minimalny powtarzalny przykład mojego kodu roboczego w 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_())

I jest to nie działający w PYSIDE2 po zmianieniu importu odpowiednio do PYSIDE2, a także nazwę "Pyqtigal" do "sygnału"

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_())

Więc po utworzeniu tego minimalnego przykładu zdałem sobie sprawę, że PYSIDE QFILEDIALOG powoduje zatrzymanie QTHREAD, podczas gdy Pyqt Qfiledialog nie zamarzają głównego wątku. Czy jest coś, co mogłem zrobić, aby poradzić sobie z tym w podobnej architekturze składniowej? (np nie używa "Movetotheread" lub "Qobject")

3
lone_coder 26 październik 2020, 08:08

1 odpowiedź

Najlepsza odpowiedź

Problem polega na tym, że nadpisujesz self.DataProcess za każdym razem, gdy powstaje nowy wątek, który może spowodować, że poprzedni obiekt będzie zebrany na śmieci przez Pythona przed QT ma szansę go usunąć. Może to spowodować zrzut rdzeniowy, jeśli QT próbuje usunąć obiekt, który już nie ma. Problemy tego rodzaju są dość powszechne zarówno w Pyqt, jak i wesde, i są zwykle spowodowane przez nie utrzymywanie odpowiednich odniesień do obiektów zależnych. Normalnym rozwiązaniem jest zapewnienie, że obiekty dotknięte podano rodzic i, jeśli to konieczne, wyraźnie je usunąć w odpowiednim czasie.

Oto jeden sposób, aby naprawić swój przykład:

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()

Trudno powiedzieć dokładnie, dlaczego pyside zachowuje się inaczej do Pyqt w tym konkretnym przypadku. Zwykle sprowadza się do różnic niskiego poziomu między dwoma wdrożeniami. Prawdopodobnie istnieją równoważne przypadki, które wpływają na pyqt, ale nie pyside. Jeśli jednak będziesz zarządzać referencjami obiektów i czyszczenia ostrożnie, takie różnice można zazwyczaj wyeliminować.

2
ekhumoro 26 październik 2020, 18:02