Не удается перезапустить поток (но при отладке все в порядке) - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь создать базу для моей программы, есть главное окно с кнопкой, которая запускает новый поток для индикатора выполнения с кнопкой вычисления, и расчет также находится в новом потоке. Первый запуск расчета в порядке, но когда я нажимаю «Открыть панель вычислений» во второй раз, программа вылетает. Я пытался поймать, где ошибка с помощью отладки, но при отладке все работает нормально. Я предполагаю, что я использую QThread неправильно ... Как улучшить мой код?

# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
        self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
        self.open_progress_bar_button.move(100, 50)
        self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)

        self.setGeometry(500, 300, 300, 100)
        self.setWindowTitle('MainWindow')
        self.show()

    def run_calculation_thread(self):
        self.open_progress_bar_button.setEnabled(False)

        self.progress_bar_thread = QThread()

        self.calculation_progress_bar = CalculationProgressBar()
        self.calculation_progress_bar.moveToThread(self.progress_bar_thread)
        QtWidgets.qApp.aboutToQuit.connect(self.progress_bar_thread.quit)
        self.progress_bar_thread.start()

        self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)

    def on_close_create_progress_bar_thread(self):
        self.progress_bar_thread.terminate()  # maybe error is here
        self.open_progress_bar_button.setEnabled(True)


class CalculationProgressBar(QtWidgets.QWidget):
    request_calculation = pyqtSignal()
    close_progress_bar = pyqtSignal()

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)

        self.calculation_thread = QThread()

        self.calculation = Calculation()
        self.calculation.notify_progress.connect(self.on_progress)
        self.calculation.calculation_done.connect(self.on_finish)

        self.request_calculation.connect(self.calculation.calculate)
        self.calculation_thread.started.connect(self.calculation.start)

        self.calculation.moveToThread(self.calculation_thread)

        QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)

        self.calculation_thread.start()

        self.setup_gui()

        self.show()

    def setup_gui(self):
        l = QtWidgets.QVBoxLayout(self)

        self.progress_bar = QtWidgets.QProgressBar(self)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.setRange(0, 100)
        l.addWidget(self.progress_bar)

        self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
        l.addWidget(self.open_progress_bar_button)

        self.setFixedWidth(300)

    def on_progress(self, i):
        self.progress_bar.setValue(i)

    def on_finish(self):
        self.close()
        self.close_progress_bar.emit()

    @pyqtSlot()
    def tables_creation_requested(self):
        self.request_calculation.emit()
        self.open_progress_bar_button.setEnabled(False)


class Calculation(QObject):
    notify_progress = pyqtSignal(int)
    calculation_done = pyqtSignal()

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)

    @pyqtSlot()
    def start(self):
        print("Ready to calculate")

    @pyqtSlot()
    def calculate(self):
        for i in range(2):
            self.notify_progress.emit(i*50)
            sleep(1)
        self.calculation_done.emit()


if __name__ == "__main__":
    application = QtWidgets.QApplication(argv)
    main_window = MainWindow()
    main_window.show()
    exit(application.exec_())

enter image description here

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Вам следует прочитать документацию QThread , и вы поймете, как это работает.

MyWin class - ваша начальная точка.

Создайте класс MyTaskгде метод "run" выполняет "тяжелые" вычисления и выдает новое значение с простым QtCore.pyqtSignal - self.updatebar.emit.

, если вы понимаете код ниже, чем вы могли бы создать QDialog ,где логика ниже может быть вставлена.

Код:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *;
import sys, time

# this code was generated in designer
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(217, 138)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.bar_btn = QtWidgets.QPushButton(self.centralwidget)
        self.bar_btn.setGeometry(QtCore.QRect(20, 20, 101, 23))
        self.bar_btn.setObjectName("bar_btn")
        self.bar = QtWidgets.QProgressBar(self.centralwidget)
        self.bar.setGeometry(QtCore.QRect(30, 60, 118, 23))
        self.bar.setProperty("value", 24)
        self.bar.setObjectName("bar")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.bar_btn.setText(_translate("MainWindow", "open bar"))

# Your 'calc' thread
class MyTask(QtCore.QThread):
    updatebar = QtCore.pyqtSignal(object)
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        for i in range(101):
            time.sleep(0.01)
            self.updatebar.emit(i)

class MyWin(QtWidgets.QMainWindow):

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent); self.ui = Ui_MainWindow(); self.ui.setupUi(self)

        # make a task
        self.my_task = MyTask()
        self.my_task.updatebar.connect(lambda val: self.ui.bar.setValue(val))
        self.ui.bar_btn.clicked.connect(self.my_task.start)

        # optional - disable button
        self.my_task.started.connect(lambda : self.ui.bar_btn.setEnabled(False))
        self.my_task.finished.connect(lambda : self.ui.bar_btn.setEnabled(True))

if __name__=="__main__":
    app = QtWidgets.QApplication(sys.argv)
    myapp = MyWin()
    myapp.show()
    sys.exit(app.exec_())
0 голосов
/ 24 января 2019

Почему вы создаете 2 темы?

Задача CalculationProgressBar не является тяжелой, поскольку его задача состоит только в том, чтобы показать данные, которые он получает из другого потока, кроме того, что графический интерфейс не может перейти в другой поток. Вам просто нужно создать отдельный поток , в котором будет жить объект класса Calculation.


Нет необходимости подключать сигнал запуска для запуска задачи, достаточно вызвать сигнал сигналом, как показано. С другой стороны, нет необходимости создавать CalculationProgressBar при каждом нажатии кнопки, лучше всего использовать его повторно.

Учитывая вышеизложенное, решение выглядит так:

# -*- coding: utf-8 -*-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QThread
from PyQt5 import QtWidgets
from sys import exit, argv
from time import sleep


class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.open_progress_bar_button = QtWidgets.QPushButton('Open calculate bar', self)
        self.open_progress_bar_button.resize(self.open_progress_bar_button.sizeHint())
        self.open_progress_bar_button.move(100, 50)
        self.open_progress_bar_button.clicked.connect(self.run_calculation_thread)

        self.calculation_progress_bar = CalculationProgressBar()
        self.calculation_progress_bar.close_progress_bar.connect(self.on_close_create_progress_bar_thread)

        self.setGeometry(500, 300, 300, 100)
        self.setWindowTitle('MainWindow')
        self.show()

    def run_calculation_thread(self):
        self.open_progress_bar_button.setEnabled(False)
        self.calculation_progress_bar.progress_bar.reset()
        self.calculation_progress_bar.show()

    def on_close_create_progress_bar_thread(self):
        self.open_progress_bar_button.setEnabled(True)


class CalculationProgressBar(QtWidgets.QWidget):
    request_calculation = pyqtSignal()
    close_progress_bar = pyqtSignal()

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)

        self.calculation_thread = QThread(self)
        self.calculation_thread.start()

        self.calculation = Calculation()
        self.calculation.moveToThread(self.calculation_thread)
        self.calculation.notify_progress.connect(self.on_progress)
        self.calculation.calculation_done.connect(self.on_finish)

        self.request_calculation.connect(self.calculation.calculate)
        QtWidgets.qApp.aboutToQuit.connect(self.calculation_thread.quit)
        self.setup_gui()

    def setup_gui(self):
        l = QtWidgets.QVBoxLayout(self)

        self.progress_bar = QtWidgets.QProgressBar(self)
        self.progress_bar.setTextVisible(False)
        self.progress_bar.setRange(0, 100)
        l.addWidget(self.progress_bar)

        self.open_progress_bar_button = QtWidgets.QPushButton("Calculate", self, clicked=self.tables_creation_requested)
        l.addWidget(self.open_progress_bar_button)
        self.setFixedWidth(300)

    def reset(self):
        self.progress_bar.setValue(0)

    @pyqtSlot(int)
    def on_progress(self, i):
        self.progress_bar.setValue(i)

    @pyqtSlot()
    def on_finish(self):
        self.hide()
        self.close_progress_bar.emit()
        self.open_progress_bar_button.setEnabled(True)

    @pyqtSlot()
    def tables_creation_requested(self):
        self.request_calculation.emit()
        self.open_progress_bar_button.setEnabled(False)


class Calculation(QObject):
    notify_progress = pyqtSignal(int)
    calculation_done = pyqtSignal()

    @pyqtSlot()
    def calculate(self):
        for i in range(3):
            self.notify_progress.emit(i*50)
            sleep(1)
        self.calculation_done.emit()


if __name__ == "__main__":
    application = QtWidgets.QApplication(argv)
    main_window = MainWindow()
    main_window.show()
    exit(application.exec_())
...