How to create multiply tabs for class in tkinter - python

I'm trying to create tabs for each class in tkinter. But faced with a problem that they are laying on each other.
I already tried to create a new class for Notebook, but the result is the same. Now I stopped at this variant.
from tkinter import *
from tkinter import ttk
class Application(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.master = master
self.mainframe = ttk.Frame(master, padding='10 10 15 15')
self.mainframe.grid(column=0, row=0, sticky=N+S+W+E)
self.connectionCheck()
self.connectionConf()
self.measureFrame()
self.meas()
self.logview()
self.running = None
#It have other functions. But I think they not really necessary
class Mapframe(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.master = master
self.mapframe = ttk.Frame(master, padding='10 10 15 15')
self.mapframe.grid(column=0, row=0, sticky=N + W + E + S)
self.choosefiles()
self.meas()
window = Tk()
notebook = ttk.Notebook(window)
tab1 = Application(notebook)
tab2 = Mapframe(notebook)
notebook.add(tab1, text='Tab1')
notebook.add(tab2, text='Tab2')
notebook.grid(row=0, column=0)
notebook.mainloop()
notebook.quit()
So, as I say they lay on each other. And the tabs not even showing when the program is launch.
Upd: after change master to self.

When creating a class that inherits from a Frame, with the intention of holding other widgets, a cardinal rule is that all child widgets should be children of that class.
In your case, you're not doing that. You're creating a notebook, adding frames to the notebook, but the widgets inside each frame are being set as children of the root window. They need to be children of the frames (eg: self).
You need to define self.mainframe and self.mapframe like in the following example. Note the use of self rather than master as the first parameter:
self.mainframe = ttk.Frame(self, padding='10 10 15 15')
...
self.mapframe = ttk.Frame(self, padding='10 10 15 15')
The only place you should be using master as the parent of a widget is when calling ttk.Frame.__init__(self, master). At no other time should you be putting widgets inside of master

Related

TKInter GUI appearance containing Notebook changes when I change class name but not parameters

I'm trying to make application with Notebook Tabs, but each tab should be described separately as class. For start I gave 2 classnames for 2 tabs as Frame1 and Frame2, but I want to give sensible names.
Here is the code that works:
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry('400x400')
root.title('Title')
notebook = ttk.Notebook(root)
class Frame1(Frame):
def __init__(self, container):
super().__init__(container)
self.Frame1 = Frame(container)
self.Frame1.config(bg='blue')
self.Frame1.place(x=0, y=24, relwidth=0.9, relheight=0.9)
class Frame2(Frame):
def __init__(self, container):
super().__init__(container)
self.Frame2 = Frame(container)
self.Frame2.config(height=200, width=203, bg= 'green')
self.Frame2.place(x=0, y=24)
Frame1 = Frame1(notebook)
notebook.add(Frame1, text = "Connection")
Frame2 = Frame2(notebook)
notebook.add(Frame2, text = "Transient Response")
notebook.place(x=10, y=10)
root.mainloop()
Result is on screenshot - 2 tabs with blue and green filling.
I want to give sensible names. As soon as I change class name, e.g. Frame2 for Frame3, picture spoils (see screenshot).
You should not be creating a frame inside your frame classes. Instances of Frame1 and Frame2 are already frames. And if you do add a frame inside of Frame1 and Frame2, they need to be children of self, not container.
Also, you're using the name Frame1 both for the name of the class and the instance of the class. You should not do that. You should name the instance with a lowercase first letter according to PEP8.
class Frame1(Frame):
def __init__(self, container):
super().__init__(container, bg='blue')
frame1 = Frame1(notebook)
Note that the instance of Frame1 is itself a frame just like any other frame. If you want to configure any of the attributes, you can do so like in my example where I set the background when calling super, but you can also call the configure method on self at any time:
class Frame1(Frame):
def __init__(self, container):
super().__init__(container, bg='blue')
self.configure(width=200, height=200)
You can also configure it outside the class just like you can with any other widget:
frame1 = Frame1(notebook)
...
frame1.configure(background="bisque")

Is there a way i can place tkinter buttons next to each other using .pack() while keeping them center justified?

I would like to have two Tkinter buttons (ttk.Button) next to each other under my entry widgets, while still keeping them centre justified. I am already using .pack() for the other widgets in the frame, so I cannot use .grid() or .place() on them. I know that you can use tk.LEFT, tk.RIGHT and so on to place them in a line, but that moves them to the far edge. Is there a way I can use a method like this to place them next to each other in the centre of the window?
this is my code:
class EmailPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
send_button = ttk.Button(self, text='Send Email\'s', command=lambda: send_email())
send_button.pack(padx=10, pady=5)
test_button = ttk.Button(self, text='Test Email', command=lambda: test_email())
test_button.pack(padx=10, pady=5)
Thanks in advance
You can create a tk.Frame and then pack the buttons in them:
from tkinter import ttk
import tkinter as tk
root=tk.Tk()
tk.Label(root,text='These 2 buttons are in centre!').pack()
f1=tk.Frame(root)
f1.pack(expand=1)
b1=ttk.Button(f1,text='Button 1')
b1.pack(expand=True,side=tk.BOTTOM)
b2=ttk.Button(f1,text='Button 2')
b2.pack(expand=True,side=tk.BOTTOM)
root.mainloop()
I have copied your code and appended some necessary methods to make it a working example.
In your example, your button commands do not need lambda since you are not sending data to those methods.
Also, self. needs to be prepended to widgets and tk.Frame needs to be managed.
import tkinter as tk
from tkinter import ttk
class EmailPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
entry = tk.Entry( self, width = 40 )
entry.pack(fill = tk.BOTH, expand = False)
self.send_button = ttk.Button(self, text='Send Email\'s', command = self.send_email)
self.send_button.pack(side=tk.RIGHT, fill = tk.BOTH, expand=True)
self.test_button = ttk.Button(self, text='Test Email', command = self.test_email)
self.test_button.pack(side=tk.LEFT, fill = tk.BOTH, expand=True)
self.pack(fill = tk.BOTH, expand = True)
def send_email( self ):
pass
def test_email( self ):
pass
my_emailer = EmailPage(tk.Tk(), "")
my_emailer.master.mainloop()

tkinter - Cant hide nested toplevel window

I create an instance of TopLevel class inside another class, but can't "withdraw()" the child window
with a call from parent.
import tkinter as tk
class Table(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self,master)
self.var_new_t=tk.Toplevel(self.master)
self.t2=Table_2(self.var_new_t)
#Goal is to create a child window and immediately hide it.
self.t2.hide_me()
#self.t2.withdraw() ##Tried a few differnt ways
class Table_2(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self,master)
self.master = master
label = tk.Label(self.master, bg='green', text='Second Table')
label.grid()
def hide_me(self):
self.master.withdraw()
root = tk.Tk()
n= Table(root)
tk.Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
I tried a few other variations to no avail.
Your code is creating two windows. Consider this code:
Table is a toplevel window because it inherits from Toplevel. So, this line creates the Table window:
tk.Toplevel.__init__(self,master)
Then, you create another window when you do this:
self.var_new_t=tk.Toplevel(self.master)
If you inherit from Toplevel you typically shouldn't create a second Toplevel inside unless you explicitly intend to create two windows.
Your code needs to look something like this:
class Table(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self,master)
self.t2=Table_2(self)
self.t2.hide_me()
class Table_2(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self,master)
self.master = master
label = tk.Label(self, bg='green', text='Second Table')
label.grid()
def hide_me(self):
self.withdraw()

Passing values from child window to parent in tkinter (python3): unexpected behavior

I am trying to write a python GUI with tkinter that, among other things, will work with values entered into pop-up 'child' window. Here is a very stripped down version of the GUI:
from minimalChild import Child
import tkinter as tk
class exGui(tk.Tk):
def update(self):
self.amount = Child(self)
print(self.amount)
def __init__(self):
tk.Tk.__init__(self)
frame = tk.Frame(self)
frame.grid()
self.amount = 0
self.button = tk.Button(frame, text="pay", command=self.update).grid(row=0, column=0)
self.button = tk.Button(frame, text="Quit", fg="red", command=frame.quit).grid(row=0, column=1)
def main():
exGui().mainloop()
if __name__ == '__main__':
main()
and a minimal version of the Child class:
import tkinter as tk
class Child(tk.Toplevel):
def submitFunction(self):
amount = 2
self.parent.amount= amount
self.destroy()
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.frame = tk.Frame(self)
self.frame.grid()
submit = tk.Button(self.frame, text='Submit', command = self.submitFunction).grid(row=0, column=0)
Evidently I am not understanding the flow in the update method, since print(self.amount) does not give the expected value '2'. Rather, it gives some float number that appears before I click the 'submit' button and is different every time I run the code. Is there some way to tell the main window to wait for Child to return a value? Also, can anyone explain what that decimal number is? I would have expected the print statement to at least return 0?
I have looked at this thread Pass user input from child to parent window python tkinter and still could not understand how to solve the problem.
Any help, criticism or pointers to references would be appreciated!
Welcome to Stackoverflow. Thanks for the clear concise question.
When you assign Child to self.amount in your update method you print it out immediately. On my console I get .!Child printed. The method completes it doesn't wait for any action in Child. It creates the window, prints the value of amount and then waits for user interaction with either of the windows. In the update method you have set amount to be the object created by Child().
What you wrote probably does set parent.amount to 2 but it does it after you printed the result.
I've added a result label to your main window, which initially shows 0. Submitting from Child sets it to 2.
import tkinter as tk
class Child(tk.Toplevel):
def submitFunction(self):
amount = 2
self.parent.amount.set(str( amount)) # Set parent amount.
self.destroy()
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.frame = tk.Frame(self)
self.frame.grid()
submit = tk.Button(self.frame, text='Submit', command = self.submitFunction).grid(row=0, column=0)
class exGui(tk.Tk):
def update(self):
self.kid = Child(self)
print(self.amount, self.kid, self.amount.get())
def __init__(self):
tk.Tk.__init__(self)
frame = tk.Frame(self)
frame.grid()
self.amount = tk.StringVar() # Make amount a StringVar
self.amount.set('0') # Set it to 0
self.button = tk.Button(frame, text="pay", command=self.update).grid(row=0, column=0)
self.button = tk.Button(frame, text="Quit", fg="red", command=frame.quit).grid(row=0, column=1)
self.result = tk.Label(frame, textvariable = self.amount).grid(row=0, column=2) # Add a result label.
# This is linked to the self.amount StringVar.
def main():
exGui().mainloop()
I hope this is clear enough.

Python tkinter 2 frames merge together

Hey guys I have to classes that both create a Frame. The first one contains a button that is supposed to close its frame. The second frame simply contains a Label. My code should first create the frame with the button and when the button is pressed the second window should show up. What happens is that when pressing the button a "merged" window is created that contains the button and the label.
import tkinter as tk
class Window1(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.btn = tk.Button(self,text = "button",command = self.run)
self.btn.grid(row = 0,column = 0)
def run(self):
tk.Frame.quit(self)
class Window2(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.label = tk.Label(self,text = "label ")
self.label.grid(row = 0,column = 0)
w = Window1()
w.mainloop()
v = Window2()
v.mainloop()
The first picture is before you press the button, the next one after you pressed the button. The problem seems that tk.Frame.quit(self) doesn't work correctly. I tried similar ways to close the window such as:
tk.Frame.destroy(self)
but that doesn't help either.
edit: I solved it by inheriting the class from tk.TK instead of tk.Frame
Frame doesn't create window - it only group elements. Tk() creates window.
To close window you have to destroy() object create by Tk(). But you don't creat it manually root = tk.Tk() so tkinter create it automatically, but you have no access to this root to close it.
If widget doesn't have parent then it uses root and your Frame does it too.
import tkinter as tk
class Window1(tk.Frame):
def __init__(self, master):
# send `root` to `Frame` as its parent
tk.Frame.__init__(self, master)
# `Frame` will keep `master as `self.master`
# so we don't have to do `self.master = master` manually
self.grid()
self.btn = tk.Button(self, text="Hello Button", command=self.run)
self.btn.grid(row=0, column=0)
def run(self):
# use `master` (`root`) to destroy it
self.master.destroy()
class Window2(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.grid()
self.label = tk.Label(self, text="Hello Label")
self.label.grid(row=0, column=0)
root = tk.Tk() # create main window as `root`
Window1(root) # send `root` to `Window1` and later to `Frame`
root.mainloop()
root = tk.Tk()
Window2(root)
root.mainloop()

Categories

Resources