Python Forum

Full Version: Tkinter classes
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello everyone.

I'm sorry to bother y'all with my stupid questions, but I just can't get some simple stuff and hours of search doesn't help me. :(

I'm trying to create classes for my GUI, so I could initialize main window through "w = Window()", not "w = tk.Tk()".
Window class should hold my default options for the window (i.e. "self.geometry("640x480" and etc.).
Next, I'd like to create inner class in the Window's class - "Form", and then inner classes for buttons and etc. in this form.

Is this the right idea of organizing my GUI?
I'd like to make my code good to understand for others and keep thing organized.
Also, one of the key features that I need is ability to access these elements from any other part of the code after initialization.

I've started to create a Window class and got some problems from the start.

My code:
import tkinter as tk


class Window(tk.Tk()):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


if __name__ == "__main__":
    w = Window()
    w.mainloop()
Errors:
Quote:Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.2\helpers\pydev\pydevd.py", line 1758, in <module>
main()
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.2\helpers\pydev\pydevd.py", line 1752, in main
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.2\helpers\pydev\pydevd.py", line 1147, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Program Files\JetBrains\PyCharm Community Edition 2019.1.2\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:/Users/operator/PycharmProjects/forms/newforms.py", line 5, in <module>
class Window(tk.Tk()):
File "C:\Users\operator\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 2023, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
TypeError: create() argument 2 must be str, not tuple

Thanks in advance for help!
When you inherit from another class you don't call it
simply change
class Window(tk.Tk()):
to
class Window(tk.Tk):
and your code will work.

Here is an example of your previous post code with the forms as separate classes
import tkinter as tk


class MainFrame(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form1 = Form1(self, background="pink")
        self.form2 = Form2(self, background="lightblue")
        self.form2.btn.bind("<Button-1>", self.on_form2_button)

    def on_form2_button(self, event):
        print("form2 button clicked")


class Form1(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1)
        lbl = tk.Label(self, text="Form1")
        lbl.pack(expand=1)


class Form2(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH)
        self.btn = tk.Button(self, text="Form2")
        self.btn.pack()


if __name__ == "__main__":
    main_frame = MainFrame()
    main_frame.mainloop()
Yoriz, thank you!
I've tried that structure out but can't reach button through "w.FrameBottom.ButtonSend" command now.
What am I doing wrong?
import tkinter as tk


class Window(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.geometry("640x480")
        self.minsize(width=640, height=480)
        self.FrameMain = self.Frame(self, background="pink").main()
        self.FrameBottom = self.Frame(self, background="lightblue").bottom()

    class Frame(tk.Frame):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.ButtonSend = None

        def main(self):
            return self.pack(fill=tk.BOTH, expand=1)

        def bottom(self):
            self.ButtonSend = self.Button(self, text="Send").send()
            return self.pack(fill=tk.BOTH)

        class Button(tk.Button):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)

            def send(self):
                return self.pack(side="right")


if __name__ == "__main__":
    w = Window()
    w.mainloop()
You don't put classes in classes in classes, make independent classes and then make instances of those classes, compare you code to my last post.

Note: you don't need to make a class of everything, making a class of frame makes sense as you are altering the standard frame by adding attributes to it, if you are not altering a button or giving it attributes you can just make an instance of a standard button.
I've tried to add "Notebook" widget, but it takes more place than it's master frame (FrameAction). Why is it so and can I fix that?
import tkinter as tk
from tkinter import ttk as tktw

class Window(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.geometry("640x480")
        self.minsize(width=640, height=480)
        self.fm = FrameMain(self, background="pink")
        self.fb = FrameBottom(self, background="lightblue")
        self.fl = FrameLeft(self.fm, background="yellow")
        self.fr = FrameRight(self.fm, background="green")
        self.fa = FrameAction(self.fr, background="orange")
        self.fc = FrameChat(self.fr, background="blue")
        self.tm = TopMenu(self)
        self.config(menu=self.tm)
        self.ab = ActionBar(self.fa)
        self.ai = ActionInfo(self.ab)
        self.ab.add(self.ai, text="Info")


class FrameMain(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1)


class FrameLeft(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1, side="left")


class FrameRight(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1, side="right")


class FrameChat(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1, side="bottom")


class FrameAction(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1, side="top")


class ActionBar(tktw.Notebook):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1)


class ActionInfo(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH, expand=1)


class FrameBottom(tk.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(fill=tk.BOTH)
        self.bs = ButtonSend(self, text="Send")


class ButtonSend(tk.Button):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.pack(side="right")


class TopMenu(tk.Menu):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.add_cascade(label="Menu")


if __name__ == "__main__":
    w = Window()
    w.mainloop()
[Image: notebook.png]
I don't know what is the expected look of your windows, the notebook widget is set to take up all the available space by self.pack(fill=tk.BOTH, expand=1)
You are creating classes just for the sake of creating classes, over complicating the code.
(May-26-2019, 11:59 AM)Yoriz Wrote: [ -> ]I don't know what is the expected look of your windows, the notebook widget is set to take up all the available space by self.pack(fill=tk.BOTH, expand=1) You are creating classes just for the sake of creating classes, over complicating the code.
I'm expecting to left part (FrameLeft (yellow)) have half of the window's width and right part (FrameRight (blue frame and Notebook)) take the other half.
The blue frame and Notebook's frame should take equal size.
How can I do that?
Putting Notebook into top-right frame takes additional size, so it takes space from the other frames.
I'm not creating classes just for the sake of creating classes. I'm planning on using all these frames.
(May-26-2019, 01:38 PM)Gupi Wrote: [ -> ]I'm expecting to left part (FrameLeft (yellow)) have half of the window's width
It does
(May-26-2019, 01:38 PM)Gupi Wrote: [ -> ]and right part (FrameRight (blue frame and Notebook)) take the other half.
Also does
(May-26-2019, 01:38 PM)Gupi Wrote: [ -> ]The blue frame and Notebook's frame should take equal size.
They do
This is no longer about classes , is the classes question resolved ?
If it is this is now a new question about how to layout frames.
Oh, I'm sorry, classes' question is solved. Thanks again for helping me out!
I'll try to explain my problem in the other thread.