Search code examples
pythonanacondapyqt5cx-freezeqthread

cx_Freeze with PyQt5 and Threading


If I run the thread_test.py code below, my function in the thread runs fine. However, if I compile it with cx_Freeze, then it gets hung up at the call to glob. It doesn't give an error and the gui remains responsive, but the thread just seems to hang (do nothing). I've tested several other functions besides glob as well, so glob itself is not the issue. I tried adding freeze_support from multiprocessing, even though I'm not using multiprocessing, but that didn't help. Are there any ideas about what might cause this? Could it have something to do with using Anaconda instead of vanilla python?

I realize that using log like this is not probably thread-safe, but for this demonstration it is fine.

For reference, I'm using:

  • Windows 7 Enterprise, Service Pack 1
  • Anaconda 4.2.0 (Python 2.7.12)
  • cx_Freeze 5.0
  • PyQt5 5.6.0

thread_test.py:

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot

import sys
import time
import glob
import logging

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
log.addHandler(logging.StreamHandler())

class worker_object(QObject):
    finished = pyqtSignal()

    def __init__(self):
        super(self.__class__, self).__init__()
        log.info('2. Worker Object Initialized')

    @pyqtSlot()
    def run_thread(self):
        log.info('5.  Worker 1/5')
        time.sleep(0.5)
        log.info('    Worker 2/5')
        glob.glob('*.jpg')
        log.info('    Worker 3/5')
        time.sleep(0.5)
        log.info('    Worker 4/5')
        self.finished.emit()
        log.info('    Worker 5/5')

class test_window(QMainWindow):
    def __init__(self):
        super(self.__class__, self).__init__()
        log.info('1. Window Initialization (Started)')

        self.centralwidget = QtWidgets.QWidget(self)
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setText('Start!')
        self.verticalLayout.addWidget(self.pushButton)

        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setRange(0,1)
        self.verticalLayout.addWidget(self.progressBar)

        self.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(self)
        self.setStatusBar(self.statusbar)

        self.pushButton.clicked.connect(self.execute)

        # Setup threading
        self.thread = QThread()
        self.worker_object = worker_object()
        self.worker_object.moveToThread(self.thread)
        self.worker_object.finished.connect(self.onFinished)
        self.thread.started.connect(self.worker_object.run_thread)
        log.info('3. Window Initialization (Finished)')

    def onFinished(self):
        self.thread.quit()
        self.statusbar.showMessage('Finished!')
        self.setEnabled(True)
        self.progressBar.setRange(0,1)
        log.info('6.  Thread Finished')

    def execute(self):
        self.statusbar.showMessage('Working')
        self.setEnabled(False)
        self.progressBar.setRange(0,0)
        log.info('4.  Thread Starting')
        self.thread.start()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    form = test_window()
    form.show()
    app.exec_()

setup.py:

from cx_Freeze import setup, Executable

buildOptions = dict(packages = [], excludes = [], includes = [], include_files = [])

executables = [Executable('thread_test.py', base=None)]

setup(name='thread_test',
      version = '1.0',
      description = 'Threading Test',
      options = dict(build_exe = buildOptions),
      executables = executables)

Solution

  • For what it's worth, I just installed Anaconda3 (Anaconda 4.2.0 with Python 3.5.2) and installed cx_Freeze. Using this, everything works fine. So maybe it is a bug in cx_Freeze on python 2.7, I'm not sure.