.ui files with fbs

Heads up! You've already completed this tutorial.

MJ_McLaren | 2020-05-07 16:49:19 UTC | #1

https://www.pythonguis.com/courses/qt-creator/embed-pyqtgraph-custom-widgets-qt-app/

I'm trying to integrate this technique with fbs but can't seem to make it work. It doesn't see my .ui file. Can you please explain how to set this up in fbs?


mike2750 | 2020-07-23 13:47:22 UTC | #2

@MJ_McLaren Its probably due to the way the file is being called i learned the below techniques from trial and error and loading it this way loads it from a relative path which works in fbs when run from source in pycharm and fbs and when frozen

This would be the fbs way to do that tutorial with fbs in mind and if the mainwindow.ui file is in same path as main.py and also in "src/main/resources/base/mainwindow.ui"

python
# from fbs_runtime.application_context import is_frozen
# from fbs_runtime.excepthook.sentry import SentryExceptionHandler
import os
import sys
import requests
from PyQt5 import uic, QtWidgets
from pyqtgraph import PlotWidget
import pyqtgraph as pg
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QApplication
from fbs_runtime.application_context.PyQt5 import ApplicationContext, \
    cached_property


class AppContext(ApplicationContext):
    def run(self):
        version = self.build_settings['version']
        QApplication.setApplicationName("App Name")
        QApplication.setOrganizationName("Some Corp")
        QApplication.setOrganizationDomain("example.com")
        current_version = version
        self.main_window.setWindowTitle("App Name v" + version)
        self.main_window.show()
        return self.app.exec_()

    @cached_property
    def main_window(self):
        return MainWindow(self)


qtCreatorFile = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "mainwindow.ui")  # Type your file path
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, ctx):
        super().__init__()
        self.ctx = ctx
        self.setupUi(self)


        #Put all your custom signals slots and other code here.



import sys

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

I use this same approach in my fbs app with lots of UI files.

Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

Setting up Application context with fbs and UI was a royal pita to learn but once you have a nice skeleton def save a copy somewhere cause it comes in handy. You will want to then define as much as possible in the UI file vs the python main so its cleaner and just have the logic in the python.

For reference of my setup

Main root is src/main/python

Ui files src/main/python/ui

For FBS stuff i also copy same files into this path so bundled properly when compiled src/main/resources/base/ui

To load say the about.ui file from "src/main/resources/base/ui/about.ui" via relative path "ui/about.ui" with FBS even when frozen

python
about_ui = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'ui', "about.ui")
uic.loadUi(about_ui, self)

This handy trick also works for images too :slightly_smiling_face:

loads my app logo in about dialog from relative 'images/chevron_logo.png'

python
self.about_app_logo.setPixmap(
            QPixmap(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'images', 'chevron_logo.png')))

====================================================

The best part of joining paths and that code is always works from where code is executed even when frozen and its OS agnostic so works for joining same path on Windows with path separators \ like it does with / on linux and Mac. One of the things i wish i had learned at beginning of my python adventures.

this equals path of current running script

python
os.path.abspath(os.path.dirname(sys.argv[0]))

This is joining current path + 'ui' folder + 'about.ui' filename os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'ui', "about.ui")

To experiment can use the below snippet and adjust

python
import os
import sys
print(os.path.abspath(os.path.dirname(sys.argv[0])))
print(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'ui'))
print(os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'ui', "about.ui"))

Output

python
/home/username/.config/JetBrains/PyCharm2020.1/scratches
/home/username/.config/JetBrains/PyCharm2020.1/scratches/ui
/home/username/.config/JetBrains/PyCharm2020.1/scratches/ui/about.ui

====================================================

To understand the resource files and structure for when its frozen also see. https://build-system.fman.io/manual/#resource-files

Hopefully that helps some and provides some context.

Screenshots for visualization. Selection_999(1301)|690x292 Selection_999(1302)|358x348


The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

.ui files with fbs was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.