M trying to create a desktop app but facing some problem while switching between frames using button. Its working all fine but it gives me an extra blank window(consist nothing) when I run my project.
Below is my code. Please suggest me any changes or error in my code.
import tkinter as tk
class Toplevel1(tk.Tk):
def __init__(self, top=None, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
top.geometry("600x450+306+137")
top.minsize(120, 1)
top.maxsize(1370, 749)
top.resizable(1, 1)
top.title("New Toplevel")
top.configure(background="#d9d9d9")
self.MenuFrame = tk.LabelFrame(top)
self.MenuFrame.place(relx=0.0, rely=0.0, relheight=0.989, relwidth=0.25)
self.MenuFrame.configure(relief='groove')
self.MenuFrame.configure(foreground="black")
self.MenuFrame.configure(background="#400080")
self.Button1 = tk.Button(self.MenuFrame)
self.Button1.place(relx=0.133, rely=0.067, height=24, width=107, bordermode='ignore')
self.Button1.configure(background="#00ff80")
self.Button1.configure(foreground="#000000")
self.Button1.configure(pady="0")
self.Button1.configure(text='''Button 1''')
self.Button1.configure(command= lambda : self.show_frame(ButtonOne))
self.MainWindow = tk.LabelFrame(top)
self.MainWindow.place(relx=0.267, rely=0.111, relheight=0.767, relwidth=0.7)
self.MainWindow.configure(relief='groove')
self.MainWindow.configure(foreground="black")
self.MainWindow.configure(background="#808040")
self.frames = {}
for F in (StartPage, ButtonOne):
frame = F(self.MainWindow)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# label of frame Layout 2
# second window frame page1
class ButtonOne(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Button 1 is pressed")
label.pack()
if __name__ == '__main__':
root = tk.Tk()
app = Toplevel1(root)
root.mainloop()
This causes a window to be created:
class Toplevel1(tk.Tk):
def __init__(self, top=None, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
...
...
app = Toplevel1(root)
And this causes a window to be created:
root = tk.Tk()
If you don't want Toplevel1 to be a separate window, don't inherit from tk.Tk. Instead, you can inherit from tk.Frame, and then you can call pack, place, or grid to add this to the root window.
However, it looks like you're intending for your Toplevel1 to be the true root window, so you can remove root = tk.Tk(). You can then do app.mainloop() rather than root.mainloop() You'll also have to make a few other adjustments, such as using self rather than top inside Toplevel1.__init__.
Put another way, if you want only one window then either inherit from tk.Tk or create an instance of tk.Tk, but don't do both.
As on tkinter callbacks, tk.Tk in class Toplevel1 is about the same as Toplevel1=tk.Tk() which, in a sesne opens a new window. the third line from whitespace, tk.Tk.__init__(self, *args, **kwargs), it becomes useless.
Related
I'm having trouble getting widgets to show on a frame that uses multi-level inheritance. I have these 2 classes:
class ScrollableFrame(Frame):
def __init__(self, parent, name, width=0, height=0, *args, **kwargs):
Frame.__init__(self, parent, *args, **kwargs, width=width, height=height)
self.parent = parent
self.pack_propagate(False)
self.label = Label(self, text=name)
self.label.pack()
self.sp = Separator(self)
self.sp.pack(fill="x")
self.v = Scrollbar(self)
self.v.pack(side=RIGHT, fill=Y)
self.c = Canvas(self, width=width, height=height, yscrollcommand=self.v.set)
self.interior = Frame(self.c)
self.interior.bind("<Configure>", self.reset_scrollregion)
self.interior.pack()
self.c.create_window((0, 0), window=self.interior, anchor="nw")
self.c.bind("<Configure>", lambda e: self.c.configure(scrollregion=self.c.bbox("all")))
self.c.pack(side=TOP, fill=X)
self.v.config(command=self.c.yview)
def reset_scrollregion(self, event):
self.c.configure(scrollregion=self.c.bbox("all"))
class GroupFrame(ScrollableFrame):
def __init__(self, parent, *args, **kwargs):
ScrollableFrame.__init__(self, parent, "Groups", *args, **kwargs, width=200, height=600)
self.group_list = []
# Populate Header
self.input = Entry(self, width=150)
self.input.pack()
self.input.insert(0, "Enter Group Name...")
self.submit_btn = Button(self, width=150, text="Submit", command=self.group_entry)
self.submit_btn.pack()
self.sp_header = Separator(self)
self.sp_header.pack(fill=X, pady=5)
def group_entry(self):
group = Group(self.interior, self.input.get())
self.group_list.append(group)
When I make a GroupFrame object and display it, I only get this:
No widgets that I added in the GroupFrame class show up. I think it's because in the GroupFrame class, 'self' doesn't count as a Frame since it inherits and initializes from ScrollableFrame. Is there any way to fix this or do I have to scrap the GroupFrame class and code it procedural instead?
As #jasonharper mentioned above, I missed the fact that the interior frame that was meant to be scrollable pushed the rest of the widgets out the bottom of the frame. All I had to do is put the widgets above the interior in another frame called header and in the GroupFrame class I could choose to put input and button in the header frame.
I have created a start-up canvas, which contains buttoms to shfit to two other sub-canvases. In addition, in those two sub-canvases, buttom to return to start-up canvas is created. However, after I enter the sub-canvas, I fail to return to start-up canvas. That is to say, when I click the buttom to return to the start-up canvas, it will create a start-up canvas beside the sub-canvas instead of closing the subcanvas and shifting to the start-up canvas. Is there any way to switch between canvases and return to the main canvas? Thank you!
import tkinter as tk
from tkinter import ttk
import re
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._Canvas= None
self.switch_Canvas(StartUpPage)
def switch_Canvas(self, Canvas_class):
new_Canvas = Canvas_class(self)
if self._Canvas is not None:
self._Canvas.destroy()
self._Canvas = new_Canvas
self._Canvas.pack()
class StartUpPage(tk.Canvas):
def __init__(self, master, *args, **kwargs):
tk.Canvas.__init__(self, master, *args, **kwargs)
tk.Frame(self)
tk.Label(self, text="Example").grid(column = 0, row = 0)
tk.Button(self, text="Canvas1",
command=lambda: master.switch_Canvas(PageOne)).grid(column = 0, row = 1)
tk.Button(self, text="Canvas2",
command=lambda: master.switch_Canvas(PageTwo)).grid(column = 0, row = 2)
class PageOne(tk.Canvas, tk.Tk):
def __init__(self, master, *args, **kwargs):
root = tk.Canvas.__init__(self, *args, **kwargs)
self.frame1 = tk.Frame(root, width=430)
self.frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
tk.Label(self.frame1, text="First Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self.frame1, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
class PageTwo(tk.Canvas, tk.Tk):
def __init__(self, master, *args, **kwargs):
root = tk.Canvas.__init__(self, *args, **kwargs)
self.frame2 = tk.Frame(root, width=430)
self.frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
tk.Label(self.frame2, text="Second Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self.frame2, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
So, few things:
First: There's the wrong parent/master relationship in PageOne and PageTwo in
def __init__(self, master, *args, **kwargs):
Since you're inheriting from tk.Canvas, the new instance of the class has parent master which in itself get's passed from
SampleApp's switchCanvas as Canvas_class(self) meaning master is . or the (main/root) window.
But then (we're still inside PageOne) you're making root = tk.Canvas.__init__(self, *args, **kwargs) which becomes None since initializers return None.
Then you're creating a new Frame whose parent is root which is None.
In short
PageOne and PageTwo are (also) new instances of Canvas whose parent is the main window, or the instance made by SampleApp.
root aka the Frame's parent is None (Which I even don't know how that affects it when you position it byself.frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True))
Hence I assume you're trying to making PageOne and PageTwo instances of Frame and put a Canvas inside them.
These are the few changes I made to the two classes: Their instances are now both of (type) tk.Frame that contains the other widgets.
class PageTwo(tk.Frame): # Sub-lcassing tk.Frame
def __init__(self, master, *args, **kwargs):
# self is now an istance of tk.Frame
tk.Frame.__init__(self,master, *args, **kwargs)
# make a new Canvas whose parent is self.
self.canvas = tk.Canvas(self,bg='yellow', width=430)
self.label = tk.Label(self, text="Second Canvas").pack(side="top", fill="x", pady=5)
self.button = tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage))
self.button.pack()
# pack the canvas inside the self (frame).
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
#print('is instance',isinstance(self,tk.Frame))
Second: Keeping track of which instances/objects are created, instead of continuously spawning new ones. I decided to keep it simple and create an internal dictionary whose keys are the classes of StratUpPage,PageOne or PageTwo meaning each class gets to have one instance that can be switched to.
def switch_Canvas(self, Canvas_class):
# Unless the dictionary is empty, hide the current Frame (_mainCanvas is a frame)
if self._mainCanvas:
self._mainCanvas.pack_forget()
# Modification 2: is the Class type passed is a one we have seen before?
canvas = self._allCanvases.get(Canvas_class, False)
# if Canvas_class is a new class type, canvas is False
if not canvas:
# Instantiate the new class
canvas = Canvas_class(self)
# Store it's type in the dictionary
self._allCanvases[Canvas_class] = canvas
# Pack the canvas or self._mainCanvas (these are all frames)
canvas.pack(pady = 60)
# and make it the 'default' or current one.
self._mainCanvas = canvas
Entire Code
import tkinter as tk
from tkinter import ttk
import re
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._mainCanvas= None
# The dictionary to hold the class type to switch to
# Each new class passed here, will only have instance or object associated with it (i.e the result of the Key)
self._allCanvases = dict()
# Switch (and create) the single instance of StartUpPage
self.switch_Canvas(StartUpPage)
def switch_Canvas(self, Canvas_class):
# Unless the dictionary is empty, hide the current Frame (_mainCanvas is a frame)
if self._mainCanvas:
self._mainCanvas.pack_forget()
# is the Class type passed one we have seen before?
canvas = self._allCanvases.get(Canvas_class, False)
# if Canvas_class is a new class type, canvas is False
if not canvas:
# Instantiate the new class
canvas = Canvas_class(self)
# Store it's type in the dictionary
self._allCanvases[Canvas_class] = canvas
# Pack the canvas or self._mainCanvas (these are all frames)
canvas.pack(pady = 60)
# and make it the 'default' or current one.
self._mainCanvas = canvas
class StartUpPage(tk.Canvas):
def __init__(self, master, *args, **kwargs):
tk.Canvas.__init__(self, master, *args, **kwargs)
tk.Frame(self) # Here the parent of the frame is the self instance of type tk.Canvas
tk.Label(self, text="Example").grid(column = 0, row = 0)
tk.Button(self, text="Canvas1",
command=lambda: master.switch_Canvas(PageOne)).grid(column = 0, row = 1)
tk.Button(self, text="Canvas2",
command=lambda: master.switch_Canvas(PageTwo)).grid(column = 0, row = 2)
class PageOne(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self,master, *args, **kwargs)
self.canvas = tk.Canvas(self,bg='blue', width=430)
print('got',self,master,args,kwargs)
tk.Label(self, text="First Canvas").pack(side="top", fill="x", pady=5)
tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage)).pack()
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
class PageTwo(tk.Frame): # Sub-lcassing tk.Frame
def __init__(self, master, *args, **kwargs):
# self is now an istance of tk.Frame
tk.Frame.__init__(self,master, *args, **kwargs)
# make a new Canvas whose parent is self.
self.canvas = tk.Canvas(self,bg='yellow', width=430)
self.label = tk.Label(self, text="Second Canvas").pack(side="top", fill="x", pady=5)
self.button = tk.Button(self, text="Back to start-up page",
command=lambda: master.switch_Canvas(StartUpPage))
self.button.pack()
# pack the canvas inside the self (frame).
self.canvas.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
#print('is instance',isinstance(self,tk.Frame))
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
EDIT: Visual Demo
I'm trying to edit Menu window. How can i edit it?
This is for Windows.
from tkinter import *
Window = Tk()
MB = Menu(Window)
Window.config(menu=MB)
Menubar = Menu(MB)
MB.add_cascade(label="File", menu=Menubar)
Menubar.add_command(label="New File")
#Btn1 = Button(Menubar, width=20, text="Button").pack() #I failed.
Window.mainloop()
I tried Btn1 = Button(Menubar, width=20, text="Button").pack()
But this one couldn't work.
usually you add items to menu like this - but what are looking for?
window = Tk()
mb = Menu(window)
menu_bar = Menu(mb, tearoff=0)
menu_bar.add_command(label="New")
menu_bar.add_command(label="Open")
menu_bar.add_command(label="Save")
menu_bar.add_command(label="Save as...")
menu_bar.add_command(label="Close")
menu_bar.add_separator()
menu_bar.add_command(label="Exit", command=window.quit)
mb.add_cascade(label="File", menu=menu_bar)
top.config(menu=mb)
You cannot add arbitrary widgets to a menu. To add items to a menu you must use one of the following functions available on the Menu object:
add
add_command
add_cascade
add_checkbutton
add_radiobutton
add_separator
To add a textual label you can use add_command and set the command attribute to None.
All of these are documented with the Menu widget definition itself, and on just about any site that includes widget documentation.
If you're looking for a control of Menu Bar with Buttons you can do it. All of the code will be in the same file.py .Libraries in Python3 (For Python 2 change to import Tkinter as tk). Later you can redirect to each window with each menu with buttons.
import tkinter as tk
from tkinter import ttk
Global list of titles for
nombre_ventanas={'PageOne'}
One menubar, you need to add similar for each menu that you want to display. menubar_global, menubar1, menubar2, ...
def menubar_global(self, root, controller):
menubar_global = tk.Menu(root)
page_1 = tk.Menu(menubar_global, tearoff=0)
# With Submenu for page_1
page_1_1=tk.Menu(page_1 , tearoff=0)
page_1_2=tk.Menu(page_1 , tearoff=0)
Main
if __name__ == "__main__":
app = ControlWindow()
app.mainloop()
Class for windows control
class ControlWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
self._nombre_ventanas = nombre_ventanas
for F in self._nombre_ventanas:
F = eval(F)
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
# First window
self.show_frame(Login)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# Title for each page
self.title(titulo_ventanas[cont._name_for_menu_bar])
# Here you can add if-sentece for each menubar that you want
if cont._name_for_menu_bar != "PageOne":
# Menu bar
print("show_frame", cont)
menubar = frame.menubar(self)
self.configure(menu=menubar)
else:
menubar = 0
self.configure(menu=menubar)
class Ventana_proveedores(tk.Frame):
_name_for_menu_bar = "Ventana_proveedores"
_config = configuracion
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tk.Button(self, text='Regresar', font=FONT_LABELFRAME,
command=lambda: controller.show_frame(Ventana_gerente)).grid(row=0, column=0)
def menubar(self, root):
return menubar_global(self, root, self.controller)
Later Each Page need to have this configuration
class PageOne(tk.Frame):
_name_for_menu_bar = "PageOne"
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
def menubar(self, root):
return menubar_global(self, root, self.controller)
My question seems easy but after trying all methods, which should work, I could not forget or delete child frame.
Program is based on this ex: Switch between two frames in tkinter
My code:
import tkinter as tk
from tkinter import ttk
class myProgram(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.title(self, "myProgram")
framesForAllWindows = tk.Frame(self)
framesForAllWindows.pack(side="top")
framesForAllWindows.grid_rowconfigure(0)
framesForAllWindows.grid_columnconfigure(0)
self.frames = dict()
for pages in (checkPage, PageOne):
frame = pages(framesForAllWindows, self)
self.frames[pages] = frame
frame.grid(row=0, column=0, sticky="nsew")
print(framesForAllWindows.winfo_children())
self.show_frame(checkPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class checkPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.f = tk.Frame()
self.f.pack(side="top")
self.controller = controller
self.enterAppid = ttk.Label(text='Please enter your something: ', font=('Courier', 20))
self.enterAppid.pack(padx=50, pady=100)
self.appidVar = tk.StringVar()
self.appidEnter = ttk.Entry(width=60, textvariable=self.appidVar)
self.appidEnter.pack()
self.checkAppid = ttk.Button(text='check', command = self.checkInfo)
self.checkAppid.pack(pady=50, ipady=2)
def checkInfo(self):
self.appid = self.appidVar.get()
if(self.appid == "good"):
self.controller.show_frame(PageOne)
#self.f.pack_forget() doesn`t work
else:
print('Unknown Error')
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="You are on page One")
label.pack(pady=1,padx=10)
app = myProgram()
app.state('zoomed')
app.mainloop()
The goal is to forget all checkPage frame and move on to PageOne. When I execute the code that I currently have "You are on page one" appears in addition to all widgets from checkPage which I don`t want. Any ideas where I am making mistake?
You didn't specify your parent for the Label, Entry and Button. If you change these 3 lines it should work.
So like this:
self.enterAppid = ttk.Label(self.f, text='Please enter your something: ', font=('Courier', 20))
self.appidEnter = ttk.Entry(self.f, width=60, textvariable=self.appidVar)
self.checkAppid = ttk.Button(self.f, text='check', command = self.checkInfo)
I'm using Python and Tkinter to create a game, and I'm attempting to have different "screens" (main menu and a level editor so far) which are each a tkinter.Frame object, and in the class definition for the main menu screen (ScreenMainMenu), in __init__, on line 11, I'm attempting to use self.config() in order to change the width, height, and background color of the "main menu" tkinter.Frame. But when I run this code, the background is still grey. I'm guessing I am missing something obvious (I am still fairly new to Tkinter and classes). Any help would be much appreciated - thank you.
import tkinter as tk
WIDTH = {"MainMenu": 600, "Editor": 450}
HEIGHT = {"MainMenu": 500, "Editor": 501}
class ScreenMainMenu(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
parent.parent.geometry("{}x{}".format(WIDTH["MainMenu"], HEIGHT["MainMenu"]))
parent.config(width=WIDTH["MainMenu"], height=HEIGHT["MainMenu"])
self.config(width=WIDTH["MainMenu"], height=HEIGHT["MainMenu"], background="red")
self.grid()
self.create_widgets()
def create_widgets(self):
# self.logo = tk.Label(self, image=r"res\logo.png")
self.logo = tk.Label(self, text="Build The Galaxy")
self.button_new_game = tk.Button(self, text="New Game")
self.button_load_game = tk.Button(self, text="Load Game")
self.button_editor = tk.Button(self, text="Editor")
self.button_exit = tk.Button(self, text="Exit")
self.logo.grid(sticky="EW")
self.button_new_game.grid(row=1, sticky="EW")
self.button_load_game.grid(row=2, sticky="EW")
self.button_editor.grid(row=3, sticky="EW")
self.button_exit.grid(row=4, sticky="EW")
class ScreenEditor(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.grid()
parent.parent.geometry("{}x{}".format(WIDTH["Editor"], HEIGHT["Editor"]))
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.grid()
self.screen_open_main_menu()
def screen_open_main_menu(self):
self.screen_mainmenu = ScreenMainMenu(self)
def screen_open_editor(self):
self.screen_editor = ScreenEditor(self)
if __name__ == "__main__":
root = tk.Tk()
root.resizable(False, False)
root.title("Build The Galaxy")
main_app = MainApplication(root)
root.mainloop()
In Tkinter, normally all widgets adjust their size to fit the contents. That means, your frame is actually red (or whatever), it just fits its content. (You can check it commenting out the self.create_widgets() method.) You can force the size with the .grid_propagate() method, passing a 0 as parameter:
class ScreenMainMenu(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
parent.parent.geometry("{}x{}".format(WIDTH["MainMenu"], HEIGHT["MainMenu"]))
parent.config(width=WIDTH["MainMenu"], height=HEIGHT["MainMenu"])
self.config(width=WIDTH["MainMenu"], height=HEIGHT["MainMenu"], background="red")
self.grid()
self.create_widgets()
self.grid_propagate(0) # force the widget to a certain size
BTW, you can use super() to initialize the parent:
super().__init__(parent) # yes, without self!