0

Is it possible to add another .py file (via import) and use it as an initialisation and event handler for a PyQt5 Multiwindow project other window & main .py files?

I have a project that consists of the following files: main.py - the main app which imports the Ui...py files below & the ui_init_events.py file also

  • main.py - the main program
  • Ui_main_window_ui.py - a complied Qt UI to display the main window
  • Ui_frmConsoleLog_ui.py - a complied Qt UI to display a window with a textEdit & comboBox objects
  • ui_init_events.py - I want this file to have functions for setting each windows Ui object fields as well as contain the Ui windows object events such as btn.clicked.connect(self.SomeotherFunc)

I have found both these posts helpful but am stuck as I do not know how to reference objects other than the self object and can't find where this is explained. See: PyQt: Modifying Widget Object from another Function Multiple Windows in PyQt4

In the 2nd post they are using multi-windows (via the QDialog) object also, however they are only using a single .py file. I am using Visual Studio Code & have built the Ui files then complied them. They are works in progress so I expect to make more changes meaning they wil be overwritten so I do not want to edit these files.

I cannot work out how to reference and change the properties in another window for initialisation purposes. The thread is:

Here is the main bits. Currently I call a function to return some data using self.combobox.additems (see #1) but I think calling this over and over again from main.py somewhat decreases code readability.

(#2) Therefore I would like advice how to move all the initialisation parts of an existing PyQt window (being an imported .py file used to generate the window + controls) into a separate .py file (eg named ui_init_events.py). However in trying & researching this I do not know nor can find an example how to reference the objects using their full hierarchical naming convention. I have tried Application. QWindow., etc. I only know how to use self.... and it isn't working (of course as that would be the referring to the function itself I understand that its being called in, not the PyQt window I'm wanting to reference). See (#3) for what is not working.

Any ideas please on resources available to assist with understanding how PyQt labels its objects from the root of the object tree is appreciated as well as an example of how to set the comboBox_Selector in the Ui_frmConsoleLog window from app.py or another .py file other than from the Ui_frmConsoleLog_ui.py or within the class definition under --init--(self, parent=None): ?

The desired file structure is like this:

+-- main.py (or app.py)
      |
      +--- Ui_main_window_ui.py (has setupui & adds objects for main window)
      |
      +--- Ui_main_window_init.py  (has init code for Ui_main_window_ui.py)
      |
      +--- Ui_main_window_events.py  (has event code for Ui_main_window_ui.py)
      |
      +--- Ui_frmConsoleLog_ui.py (has setupui & adds objects for 2nd window)
      |
      +--- Ui_frmConsoleLog_init.py  (has init code for Ui_frmConsoleLog_ui.py)
      |
      +--- Ui_frmConsoleLog_events.py  (has event code for Ui_frmConsoleLog_ui.py)

main.py file contents

# Default imports
import sys, os, io, sched, time
import ui_resources_rc

from PyQt5.QtCore import QObject, QThread, pyqtSignal
from configparser import ConfigParser
...a portion of code here uses a ini config file for retrieving/saving some settings of the UI & I want to set the properties for each object accordingly...

from PyQt5.QtWidgets import (QApplication, QDialog, QMainWindow, QMessageBox, QWidget)
from PyQt5.uic import loadUi

from Ui_main_window_ui import Ui_MainWindow
from Ui_frmConsoleLog_ui import Ui_frmConsoleLog
from ui_init_events import (funcUpdateFormCombos)

...todo... open a network connection to the system (its an inverter)

# This window will be used for filtering network poll event types
class ConsoleLogWindow(QWidget, Ui_frmConsoleLog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Console Log')
        
class AppWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.win = None  # No Console Log window visible yet.
        self.setupUi(self)

        #(1) Here is where I will have to call this a lot of times to 
        # populate the vaious window objects which I dont want to do
        self.comboBox_Selector.addItems(funcUpdateFormCombos())

    self.action_Console.triggered.connect(self.OpenConsoleLogWindow)
        self.action_About.triggered.connect(self.about)
        # Populate the controls as per the config file
        #initialise() <-- (2) Here is where I want to call a
        #                 function in ui_init_events.py to setup
        #                 the form's objects as created in QtDesigner
        #                 & exported as a .py file.  I want to continue
        #                 to edit each PyQt window later so I dont want
        #                 to add to the Ui_... files themselves but in
        #                 main.py call a function that has the code to
        #                 set the object fields like checked, combobox
        #                 list contents, etc. 

    def OpenConsoleLogWindow(self, checked):
        if self.win is None:
            self.win = ConsoleLogWindow()
            self.win.show()
        else:
            self.win.close()  # Close window.
            self.win = None  # Discard reference.

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = AppWindow()
    win.show()
    print('HERE')
    sys.exit(app.exec())

ui_init_events.py file contents (this is the file I want to house all window object setup without editing the PyQt UI converted .ui to .py files such as Ui_main_window_u.py & Ui_frmConsoleLog_ui.py which I don't want to edit at all.

# Contains the initialisation of widgets fields
def funcUpdateFormCombos():
        LIST = ['Amps', 'Volts', 'Watts']
        LIST.sort()
        return tuple(LIST)

# Would prefer to have a single file per Ui_...py file that houses
# all the events that get triggered for each Ui windows.
# How is this done? eg: <not working> (3)
def initialise()
    frmConsoleLog.self.comboBox_Selector.addItems('Watts','Amps','Volts')

Contents of Ui_frmConsoleLog_ui.py (has the combobox I want to populate)

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_frmConsoleLog(object):
    def setupUi(self, frmConsoleLog):
        frmConsoleLog.setObjectName("frmConsoleLog")
        frmConsoleLog.resize(640, 468)
        frmConsoleLog.setToolTip("")
        frmConsoleLog.setStatusTip("")
        frmConsoleLog.setAccessibleName("")
        self.horizontalLayout = QtWidgets.QHBoxLayout(frmConsoleLog)
        self.horizontalLayout.setContentsMargins(0, 1, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.comboBox_Selector = QtWidgets.QComboBox(frmConsoleLog)
        self.comboBox_Selector.setObjectName("comboBox_Selector")
        self.horizontalLayout.addWidget(self.comboBox_Selector)
        self.textEdit_ConsoleLog = QtWidgets.QTextEdit(frmConsoleLog)
        self.textEdit_ConsoleLog.setEnabled(True)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.textEdit_ConsoleLog.sizePolicy().hasHeightForWidth())
        self.textEdit_ConsoleLog.setSizePolicy(sizePolicy)
        self.textEdit_ConsoleLog.setObjectName("textEdit_ConsoleLog")
        self.horizontalLayout.addWidget(self.textEdit_ConsoleLog)

        self.retranslateUi(frmConsoleLog)
        QtCore.QMetaObject.connectSlotsByName(frmConsoleLog)

    def retranslateUi(self, frmConsoleLog):
        _translate = QtCore.QCoreApplication.translate
        frmConsoleLog.setWindowTitle(_translate("frmConsoleLog", "Console Log"))
  • I think my issue is something to do with **inheritance**, where I need to layer the code so that the functions in the code in the file ui_init_events.py can referenced or has access to change all the objects in all windows. Is that possible & advisable? – fireblade-au Feb 23 '21 at 04:11
  • Your problem has nothing to do with inheritance or OOP but with that you are confusing the objective of a function which is: to be like a black box where data (or objects) enter, process and obtain outputs. Do not mix things up or try to make the objects global as you will have errors that are difficult to debug – eyllanesc Feb 23 '21 at 04:18
  • Eliminate my answer because I consider that the space provided by SO is too small to solve your problem, besides that it involves a lot of effort that I don't want to invest to teach you that what you are trying to do is illogical and incorrect. I emphasize (I don't want to sound pedantic): I recommend you review your notes about the scope in python and why it is recommended to divide the project into several scripts. Bye. – eyllanesc Feb 23 '21 at 07:28
  • can you point me to a resource that has the info I need to understand? – fireblade-au Feb 23 '21 at 07:30
  • In app.py MainWindow class right below self.setupUi(self) I added a from init_Ui_main_window import (initMainWindow) line. In that .py file I added the initMainWindow function with an input variable name **window**. I moved all my addItems, etc into that function & used the new variable name **window** to replace self. In my app.py/main.py file right below the added from/import I added initMainWindow(self), boom it works & its a lot tidier project. I will post the answer as I dont think this is bad practice & it is likely a misunderstanding in what others think Im trying to achieve. – fireblade-au Feb 23 '21 at 08:02

1 Answers1

0

Worked it out, thanks to an answer in my previous post (that the poster removed?), here's the snippet that works and allows your functions in another .py file to set MainWindow pyQt object control properties in the parent main.py file for a much tidier project. I honestly don't care that professional devs think that is not good practice (if they do they are very closed minded).

In app.py (or main.py - I had two files and was playing with names)

class AppWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.win = None        # No Console Log window visible yet.
        self.setupUi(self)
        from init_Ui_main_window import (initMainWindow)
        initMainWindow(self)
        ...

In init_Ui_main_window.py:

def initMainWindow(window):
    funcLoadConfig()
    # Populate the controls in the main window
    window.line_Settings_Simple_Default_Selection.setText(str(SETTINGS_SIMPLE_DEFAULT_SELECTOR))

Note: SETTINGS_SIMPLE_DEFAULT_SELECTOR is a globally defined static variable that wont ever be changed as its read from a config ini file. The user will be able to change the line value if they want to override the default, but it's nice to have a default value populated.

Was that easy as when initMainWindow(self) is called self from the previous object context is passed across to the function and set in function variable window, so to reference the main windows objects its just a case of referencing window.[object name as one would in main.py]

  • To YOU who down-ticked the question: 1) you obviously did not understanding my question & 2) it's quite rude as I had researched it for a few hours prior to my 1st post, just in the wrong places. Yes it was a basic question & showed Ive not been programming with contexts period (I said learning & was stuck). I did get a suggestion from the earlier post about this whom the poster removed their answer when I replied it failed. He didnt explain it in a way I understood. We dont all think alike, I wanted to thank him but the comment was removed. – fireblade-au Feb 24 '21 at 10:22
  • From eyllanesc's comment I learning something about python which the learning content didn't cover. I just needed to pass the self object. So that downtick is seriously rude to do that & who gives YOU the right to determine if I researched a topic or not * be judge over me? Seriously – fireblade-au Feb 24 '21 at 10:22
  • Also there is nothing wrong with my answer. It works and it works well and does not cause any problems so far a coding is concerned. It is likely another person will see this post and it will help them too. – fireblade-au Feb 24 '21 at 10:23