Search code examples
pythonmultithreadingnonblocking

Threading function block main not all the time


I am working on a project in which I have a main class which create a thread for execute a very long function whereas the main class (my GUI) will continue to be available for displaying some information during the execution of this long function. So I found on Stack Overflow a way to do it:

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtWidgets import QApplication
import threading
import sys
import time
from PyQt5.QtCore import QObject, pyqtSignal


class Ui_SaftMainWindow(object):
    def setupUi(self, SaftMainWindow):
        SaftMainWindow.setObjectName("SaftMainWindow")
        SaftMainWindow.resize(178, 284)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/logo/img_Logo-E.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        SaftMainWindow.setWindowIcon(icon)
        self.gridLayout = QtWidgets.QGridLayout(SaftMainWindow)
        self.gridLayout.setObjectName("gridLayout")
        self.frame_all_pb = QtWidgets.QFrame(SaftMainWindow)
        self.frame_all_pb.setMinimumSize(QtCore.QSize(160, 0))
        self.frame_all_pb.setMaximumSize(QtCore.QSize(160, 16777215))
        self.frame_all_pb.setObjectName("frame_all_pb")
        self.gridLayout_7 = QtWidgets.QGridLayout(self.frame_all_pb)
        self.gridLayout_7.setObjectName("gridLayout_7")
        self.pb_stop_simu = QtWidgets.QPushButton(self.frame_all_pb)
        self.pb_stop_simu.setText("STOP")
        self.gridLayout_7.addWidget(self.pb_stop_simu, 5, 0, 1, 1)
        self.pb_start_simu = QtWidgets.QPushButton(self.frame_all_pb)
        self.pb_start_simu.setObjectName("pb_start_simu")
        self.pb_start_simu.setText("START")
        self.gridLayout_7.addWidget(self.pb_start_simu, 4, 0, 1, 1)
        self.gridLayout.addWidget(self.frame_all_pb, 0, 0, 1, 1)

        QtCore.QMetaObject.connectSlotsByName(SaftMainWindow)


class Solver(QObject, object):
    data_changed = pyqtSignal(str, int)

    def __init__(self):
        # Call init function of QObject class for pyQt signal
        QObject.__init__(self)


    def init_parameters(self, input_files, **keys):
        print("Some initialisation")

    def time_step(self):
        # Function with Fortran call
        print("Fortran function")

    def transient_solve(self, arg):
        print("starting")
        t = threading.currentThread()
        i = 1
        while getattr(t, "do_run", True):
            print("working on " + str(i))
            i += 1
            time.sleep(5)
        print("Stopping as you wish.")


class GuiHandler:
    def __init__(self, gui, main_window,):
        # Declarations
        self.gui = gui
        self.main_window = main_window

        self.gui.setupUi(self.main_window)
        self.gui.pb_start_simu.clicked.connect(self.pb_start_simu_clicked)
        self.gui.pb_stop_simu.clicked.connect(self.pb_stop_simu_clicked)

        # Create instance of the second class
        app = Solver()

        # Connect signal to emit signal from second class to ihm later
        app.data_changed.connect(self.myfunction)

        # Initialize some parameters of my second class
        app.init_parameters("", json_file="", output_file="")

        # Create thread object
        self.t = threading.Thread(target=app.transient_solve, args=("task",))

    def pb_start_simu_clicked(self):
        print("Start thread")
        self.t.start()

    def pb_stop_simu_clicked(self):
        print("Stop thread")
        self.t.do_run = False
        self.t.join()

    def myfunction(self, string="this_is_a_test", integer=0):
        print(str(string))
        print(str(integer))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    handler = GuiHandler(Ui_SaftMainWindow(), QtWidgets.QWidget())
    handler.main_window.show()
    sys.exit(app.exec_())

This simple example works. But, when I replace the sleep by a call to a bigger function (the time_step function which call a Fortran file compiled as Python, the GUI function is blocked)

Any idea if it is linked to my way to create and start the thread or any idea about what I have to check/find to make works my code?

I can't give you another code sample for now.


Solution

  • I find a solution, adding !f2py: threadsafe in the fortran code.