Search code examples
pythonpyqtpyqt6

How to access components from another module with PyQt6?


I made a minimum working environment for this question.

First I have this UI file (GDrive Link),which I'll later load in my UI.py.enter image description here, which I'll later load in my

The UI.py goes thus:

import sys

from PyQt6.QtWidgets import QMainWindow, QApplication, QPlainTextEdit
from PyQt6 import uic


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = uic.loadUi(r"C:\Documents\Qt Designer\mwe.ui") # Change it to yoour own .ui

        self.txtBox = self.findChild(QPlainTextEdit, 'plainTextEdit')
        self.ui.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    import main

    sys.exit(app.exec())

Essentially, what I want to do is to access this PlainTextEditor in main.py, becuase that's where I'll write the functions.

import UI

UI.Window.txtBox.insertPlainText('foo')

But when I try to run UI.py, I get this error:

Traceback (most recent call last):
  File "C:\Users\Me\PycharmProjects\dynamic_ui_foo\UI.py", line 18, in <module>
    import main
  File "C:\Users\Me\PycharmProjects\dynamic_ui_foo\main.py", line 3, in <module>
    UI.Window.txtBox.insertPlainText('foo')
AttributeError: type object 'Window' has no attribute 'txtBox'

It says Window doesn't have this attribute. How do I access the components from another module? And am I going in the right way by separating UI codes and function codes (I know the cross-importing looks terrible).


Solution

  • To illustrate your error message, consider the following example:

    class bla:
        def __init__(self):
            self.foo = 'test'
    
    
    print(bla.foo) # <-- results in your error
    
    b = bla()
    print(b.foo) # <-- this is what you would like to access
    
    

    Right now you are trying to access txtBox of the class Window, but you need to access txtBox from your instance window.

    However, I have doubts about it working in the way you do your imports. I would suggest to move

    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = UI.Window()
    
        sys.exit(app.exec())
    

    to main.py. Use UI.py to only define the layout. Changing the text txtBox can be implemented either as a method of Window:

    class Window(QMainWindow):
        # init code
        def change_content(self, content):
            self.txtBox.insertPlainText(content)
    

    Then in main you call that:

    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = UI.Window()
        window.change_content()
        sys.exit(app.exec())
    

    Of course you can use a more direct approach:

    if __name__ == "__main__":
        app = QApplication(sys.argv)
        window = UI.Window()
        window.txtBox.insertPlainText(content)
        sys.exit(app.exec())
    

    The last example seems to be easier. But that way, if you change txtBox to something else you need to keep in mind to also do changes in main.py. With the first example you only have to do changes in UI.py.

    Edit: Added missing argument self. Thanks to musicamante for pointing that out.