2

i have written a Python application and I would like to distribute it. I have built a GUI on top of it and works fine so far. I use the following to set up the GUI:

qtCreatorFile = "gui.ui" 
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)

class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        # Initialize parent PyQt classes
        QtWidgets.QMainWindow.__init__(self)
        self.setupUi(self)
        (....)

The application starts with the following:

if __name__ == "__main__":
     app = QtWidgets.QApplication(sys.argv)
     window = MyApp()
     window.show()
     sys.exit(app.exec_())  

If I understand correctly, for using the 'gui.ui' file withing packaging with fbs, I should load it as a resource. So I use these as a modification:

from fbs_runtime.application_context import ApplicationContext, cached_property #added to imports)

class AppContext(ApplicationContext):           # Subclass ApplicationContext
    def run(self):
        qtCreatorFile=self.get_design()    # to get the .ui file
        Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
        window = MyApp()
        version = self.build_settings['version']
        window.setWindowTitle("EPANET parser v" + version)
        window.show()
        return self.app.exec_()

    def get_design(self):
        qtCreatorFile=self.get_resource("gui.ui") # It is in the correct src\main\resources path
        return qtCreatorFile

    @cached_property # all tutorials suggest this , but don't understand why. ???
    def main_window(self):
        return MainWindow(self)

The fbs application should start with the following replacement of if __name__ == '__main__:':

if __name__ == '__main__':
    appctxt = AppContext()    
    exit_code = appctxt.app.exec_()
    sys.exit(exit_code)

However, I get the following error: Traceback (most recent call last): File "C:\Users\....src\main\python\main.py", line 61, in <module> class MyApp(QtWidgets.QMainWindow, Ui_MainWindow): NameError: name 'Ui_MainWindow' is not defined

I understand that the MyApp is inheriting from the Ui_MainWindow that is defined inside the AppContext class now, and MyApp cannot reference it. Any help will be greatly appreciated!

DiNik
  • 117
  • 1
  • 1
  • 7
  • 1
    I suppose you removed the loadUiType from the main indentation shown in the first snippet, right? – musicamante Dec 06 '19 at 12:51
  • Yes, I removed it! – DiNik Dec 07 '19 at 15:02
  • I should note to anyone that is seeing this question in the future, that one of my errors is that the .ui file should be in \src\main\resources\base path. The other error is solved with the accepted answer. – DiNik Dec 07 '19 at 16:25

1 Answers1

4

Instead of using uic.loadUiType() you can use uic.loadUi() that just populates the window by passing the .ui path

main.py

from fbs_runtime.application_context.PyQt5 import ApplicationContext, cached_property

import sys

from mainwindow import MyApp


class AppContext(ApplicationContext):
    def run(self):
        self.window.resize(640, 480)
        self.window.show()
        return appctxt.app.exec_()

    def get_design(self):
        qtCreatorFile = self.get_resource("gui.ui")
        return qtCreatorFile

    @cached_property
    def window(self):
        return MyApp(self.get_design())


if __name__ == "__main__":
    appctxt = AppContext()
    exit_code = appctxt.run()
    sys.exit(exit_code)

mainwindow.py

from PyQt5 import QtWidgets, uic


class MyApp(QtWidgets.QMainWindow):
    def __init__(self, ui, parent=None):
        super().__init__(parent)
        uic.loadUi(ui, self)
        # ...
└── src
    ├── build
    │   └── settings
    │       ├── base.json
    │       ├── linux.json
    │       └── mac.json
    └── main
        ├── icons
        │   ├── base
        │   │   ├── 16.png
        │   │   ├── 24.png
        │   │   ├── 32.png
        │   │   ├── 48.png
        │   │   └── 64.png
        │   ├── Icon.ico
        │   ├── linux
        │   │   ├── 1024.png
        │   │   ├── 128.png
        │   │   ├── 256.png
        │   │   └── 512.png
        │   ├── mac
        │   │   ├── 1024.png
        │   │   ├── 128.png
        │   │   ├── 256.png
        │   │   └── 512.png
        │   └── README.md
        ├── python
        │   ├── main.py
        │   └── mainwindow.py
        └── resources
            └── base
                └── gui.ui
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thank you! This solves the problem, so I close the question. Running command `fbs run` works and the GUI behaves as it should. However, with command `fbs freeze` I encounter no error, but the generated .exe does not start. It gives 'fatal error detected : failed to execute script fbs_pyinstaller_hook. Any ideas? Edit: My venv contains Python 3.6.4, PyInstaller 3.4, PyQt 5.9.2, pywin32 and the updated pandas numpy and networkx libraries. Th e first time it didnt work I got the error no module win32com, so I installed library pywin32. – DiNik Dec 07 '19 at 16:18
  • 1
    Besides installing fbs, you should manually install pyinstaller as well, for instance in anaconda: `conda install -c conda-forge pyinstaller` – dbouz Dec 08 '19 at 18:55
  • 1
    PyInstaller is installed in the virtual env I use for development. What I also tried is manually installing `pyinstaller-hooks` as suggested in this thread, but it didn't work either. https://stackoverflow.com/questions/58479154/failed-to-execute-script-fbs-pyinstaller-hook-for-pyqt5-gui-application?fbclid=IwAR2QgPYJAdWrlePodIfqiFD-3w4155fzxPD4WJJANXO2YCcefOkpMsPbCuQ – DiNik Dec 08 '19 at 19:08
  • I finally solved the problem by manually pip installing only what is required with correct versions in a new empty virtual environment (with Python 3.5.6). I do not know if the Python version was the problem, but now it works. – DiNik Dec 11 '19 at 13:42