PyQt5 multi-page application: dividing main class into several files?

Viewed 584

I am trying to build a rather complex GUI application using PyQT5 and python 3.8 and I am stuck on how to divide the code into manageable files. I am new to GUI programming and object oriented programming in general and have tried several solutions I could find on the internet, but I could get none of them to work for me. So I'm sorry, if this question has been asked lots of times before, but I think I need some hints tailored specifically to my project.

I am not yet allowed to post images, so here is a link how the app looks instead: Application GUI

My application has a sidebar, which is always visible and a stacked widget, which contains multiple pages (visible in the link above is 14 WEL). I have one main.py file, which I want to use to set up the ui and connect the buttons of all pages. Then I would like to have several .py files to handle the user input and calculations (one file per page). I also have a helper_functions.py file to store functions I need on more than one page. What worked so far is to import the page class (e.g. OutWelPageControl) into the MainWindow class within the init, but then I can't seem to call createPreview() from the getFile() within OutWelPageControl (application terminates with code=1073741845 when I click the button which triggers the getFile()). Without the call to createPreview(self, welFiles_num) it does work.

main.py:

import sys
import os
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg
from helper_functions import *

# GUI FILE
from ui_main import Ui_MainWindow

class MainWindow(qtw.QMainWindow):
    def __init__(self):
        qtw.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        ## PAGE CONNECTION
        ########################################################################
        self.ui.btn_out_wel.clicked.connect(lambda: self.ui.stackedWidget.setCurrentWidget(self.ui.page_out_wel))
        #... (other pages)

        
        # This is probably wrong
        from out_wel import OutWelPageControl
        
        ## BUTTONS
        # How I want it to work
        self.ui.btn_file.clicked.connect(lambda: OutWelPageControl.getFile(self))

        self.show()
    
if __name__ == "__main__":
    app = qtw.QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

helper_functions.py:

from main import *

class HelperFunctions(MainWindow):
    def getFile(self):
        fnames = qtw.QFileDialog.getOpenFileNames(self, 'WEL-Datei öffnen', 'C:\\',"*.WEL")[0]
        return (fnames)

Page out_wel.py:

from main import *
from helper_functions import *

class OutWelPageControl(MainWindow):
    def getFile(self):
        welFiles = HelperFunctions.getFile(self)

        # Label Input File
        welFiles_string = []
        for wel in welFiles:
            welFiles_string.append(os.path.basename(wel))
        welFiles_string2 = ', '.join(welFiles_string)
        self.ui.lbl_filepath.setText(welFiles_string2)

        welFiles_num = []
        for wel in welFiles:
            welFiles_num.append(os.path.splitext(os.path.basename(wel))[0])
        
        # Call another function
        createPreview(self, welFiles_num)
    
    def createPreview(self, welFiles_num):
        pass

So my question is basically how can I divide the application code, so that each page has its own .py file and still can change elements of the ui? Do the page files have to contain subclasses of the MainWindow or is there another option to make it work?

1 Answers

There is quite a bit of code here without one specific problem, but I can try and give some general advice and see if that helps you in any way.

  1. In helper_functions, you cannot have from main import * as when you import helper_functions in main, this leads to a circular import (they import each other over and over)
  2. Also in helper_functions, it shouldn't need to be a class if all you need are some functions available in different pages. You can just have the functions there and call them using helper_functions.func_name(args).
  3. from out_wel import OutWelPageControl should be at the top of the .py file, not inside the init of the main class
  4. For importing and using your ui code, use dual inheritance so you don't have to use self.ui everywhere:
class MainWindow(qtw.QMainWindow, Ui_MainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        self.setup_ui()
  1. You shouldn't be subclassing your main class in other classes you've built for different pages.
  2. I would have a look at this page about how to use stacked widgets. But I believe you would have to be subclassing QWidget for your different 'page' classes so that they are widgets that you can add to the stacked widget.
  3. If you are making classes, you should be instantiating objects, e.g in main you should have self.out_wel_page = OutwelPageControl()
  4. If you need some functionality from the parent widget in the child widget's function, you can always pass in self from the parent to the child

Let me know if anything from here helped, couldn't find one specific thing to fix the issues you're having but wanted to try to help out.

Related