Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pyinstaller doesn't work properly with threading

A program that runs a bat file which contains instruction for running an executable(longTask) using Qthread but it doesn't work as expected when I create an executable using Pyinstaller with the following command. I gave '--windowed' to do not provide a console window for standard i/o

pyinstaller --onefile --windowed main.py

But the interesting thing is it works as expected when I remove the --windowed argument

pyinstaller --onefile main.py

Here is the code:

from PyQt4.Qt import *
import subprocess


def callSubprocess():
    page = QWizardPage()
    page.setTitle("Run myLongTask")

    runButton = QPushButton("Run")
    progressBar = QProgressBar()
    procLabel = QLabel()
    procLabel1 = QLabel()
    progressBar.setRange(0, 1)

    layout = QGridLayout()
    layout.addWidget(runButton, 0, 0)
    layout.addWidget(progressBar, 0, 1)
    layout.addWidget(procLabel)
    layout.addWidget(procLabel1)

    # Calls thread class
    myLongTask = TaskThread()
    runButton.clicked.connect(lambda: OnStart(myLongTask, progressBar, procLabel1))
    myLongTask.taskFinished.connect(lambda: onFinished(progressBar, procLabel))
    page.setLayout(layout)
    return page


def OnStart(myLongTask, progressBar, procLabel1):
    progressBar.setRange(0, 0)
    myLongTask.start()
    # I am waiting until my subprocess completes
    while not myLongTask.isFinished():
        QCoreApplication.processEvents()
    procLabel1.setText("Hello This is main")


def onFinished(progressBar, procLabel):
        # Stop the pulsation
        progressBar.setRange(0, 1)
        procLabel.setText("longTask finished")


class TaskThread(QThread):
    taskFinished = pyqtSignal()
    def __init__(self):
        QThread.__init__(self)

    def run(self):
        proc = subprocess.Popen(r'C:\Users\Desktop\runInf.bat', bufsize=0, shell=True,  stdout=subprocess.PIPE)
        proc.wait()
        self.taskFinished.emit()

    def __del__(self):
        self.wait()


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    wizard = QWizard()
    wizard.addPage(callSubprocess())
    wizard.setWindowTitle("Example Application")
    wizard.show()

sys.exit(wizard.exec_())

When executed the above code in Pycharm it works as expected. But when built with PyInstaller, the main thread doesn't wait until the subprocess completion.

Any idea how to create an executable where threading works as expected. Thanks in advance

like image 865
Nagamani Avatar asked Jan 31 '18 07:01

Nagamani


People also ask

Why my PyInstaller is not working?

The most common reason a PyInstaller package fails is that PyInstaller failed to bundle a required file. Such missing files fall into a few categories: Hidden or missing imports: Sometimes PyInstaller can't detect the import of a package or library, typically because it is imported dynamically.

Why is PyInstaller so slow?

PyInstaller's bootloader is usually quite fast in one-dir mode, but it can be much slower in one-file mode, because it depacks everything into a temporary directory. On Windows, I/O is very slow, and then you have antiviruses that will want to double check all those DLL files. PyQt itself is a non-issue.

Does PyInstaller include all dependencies?

PyInstaller can bundle your script and all its dependencies into a single executable named myscript ( myscript.exe in Windows). The advantage is that your users get something they understand, a single executable to launch.


1 Answers

I just resolved this issue. My findings as follows.

The culprit is subprocess. So I replaced subprocess with some file handling (let's say opening a file 10000 times and write some content) and it works as expected which means the main thread waits until child process completes.

So Pyinstaller does work properly with threading

I added few more arguments

proc = subprocess.Popen(os.getcwd() + r'\runInf.bat',
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            stdin=subprocess.PIPE,
                            cwd=os.getcwd(),
                            env=os.environ)
proc.stdin.close()

This change made the subprocess to run and the main thread also waits until its completion

like image 170
Nagamani Avatar answered Sep 19 '22 16:09

Nagamani