Python Forum

Full Version: Tkinter Class Import Module Issue
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm trying to keep my code clean by separating the GUI from the logic.

Inside of the 'main.py' file, I'd like to call functions from other files that are imported to build the GUI.

The problem is that I cannot figure out how to build a GUI from the 'main.py' file when I try to call another file as an imported module.

Here's what I have in the 'main.py' file:

from tkinter import *
import create_btn

class Main(Tk):
    def __init__(self):
        super().__init__()
        self.title('Main Window')
        self.geometry('600x400')
        self.eval('tk::PlaceWindow . center')


if __name__ == '__main__':
    app = Main()
    app.mainloop()
And here is what I have in the 'create_btn.py' file:

from tkinter import *

def createBTN(self):
    self.b1 = Button(root, text='B1')
    self.b1.pack()
So, how exactly can I build a simple button from another file that I want to import into the 'main.py', or in other words, how do I get the 'create_btn.py' file to build the button inside of the 'main.py' file? A simple example would be greatly appreciated.
In main.py, you just call
create_btn.createBTN(self)
Also note that in createBTN(), there is an undefined reference to 'root'. I suppose you mean 'self'.
Thanks you so much! This worked!

Also, could you briefly explain why I have to put '(self)' into the 'create_btn.createBTN(self)' function?
I'm still trying to learn classes in both Python & Tkinter. I'm not quite sure how it relates to the class.
Thanks again!
(Sep-04-2022, 08:20 PM)AaronCatolico1 Wrote: [ -> ]Also, could you briefly explain why I have to put '(self)' into the 'create_btn.createBTN(self)' function?
The function needs at least an argument that is the parent widget of the Button it creates. You could perhaps call this argument 'parent' instead of 'self'.

When developing the function, you may find that it needs to access methods of your instance of the Main class, for example it may want to call a method of that class, in that case, it would become necessary to pass this instance. The variable name 'self' is typically used only to denote the implicit instance of methods in classes, not external functions.

Another technique that you will learn when you have more experience with classes is that of Mixin classes, which are ways of extending existing classes with new methods that may come from another file. As you are only beginning to use classes, keep it simple.
Please see the thread Namespace flooding with * imports

If your intention is to make a special version of a tk.Button that you set some attributes and then use that button in various places.
Subclass tk.Button and then you can use it in place of a normal tk.Button,
I would leave the parent to decide how the buttons are located.

specialized_btn.py
import tkinter as tk


class SpecializedButton(tk.Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # your sepecialized code here
        self.config(bg="Red")
main.py
import tkinter as tk
from specialized_btn import SpecializedButton


class MainFrame(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, *kwargs)
        specialized_button = SpecializedButton(self, text="B1")
        specialized_button.pack()


def main():
    app = tk.Tk()
    app.title("Main Window")
    app.geometry("600x400")
    app.eval("tk::PlaceWindow . center")
    main_frame = MainFrame(app)
    main_frame.pack()
    app.mainloop()


if __name__ == "__main__":
    main()
I am not seeing where you are "separating the GUI from the logic". Where is there any logic?

Or are you trying to create a layer that insulates writing a GUI program from the details of a particular GUI package? In that context "createButton()" makes a little more sense, but I would still try to implement that framework as a family of classes instead of a bunch of functions. Programmers will want to create special versions of your button (subclasses) and this is easier if you provide classes instead of functions.
Thank you for your example code!

(Sep-04-2022, 09:07 PM)Yoriz Wrote: [ -> ]Please see the thread Namespace flooding with * imports

If your intention is to make a special version of a tk.Button that you set some attributes and then use that button in various places.
Subclass tk.Button and then you can use it in place of a normal tk.Button,
I would leave the parent to decide how the buttons are located.

specialized_btn.py
import tkinter as tk


class SpecializedButton(tk.Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # your sepecialized code here
        self.config(bg="Red")
main.py
import tkinter as tk
from specialized_btn import SpecializedButton


class MainFrame(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, *kwargs)
        specialized_button = SpecializedButton(self, text="B1")
        specialized_button.pack()


def main():
    app = tk.Tk()
    app.title("Main Window")
    app.geometry("600x400")
    app.eval("tk::PlaceWindow . center")
    main_frame = MainFrame(app)
    main_frame.pack()
    app.mainloop()


if __name__ == "__main__":
    main()