In line 11 I tried to use the grid(), but it keeps giving me
"wm_grid() got an unexpected keyword argument 'row'" error
Can anyone take a look at this?
import tkinter as tk
class MainPage(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)
# pass frames as dictionaries
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
# problem here****************************************************
frame.grid(row=1, column=1)
# problem here****************************************************
self.show_frame(StartPage)
def show_frame(self, controller):
frame = self.frames[controller]
frame.tkraise()
class StartPage(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="StartPage")
label.pack(pady=10, padx=10)
root = MainPage()
root.mainloop()
I guess the issue for you comes because you are subclassing StartPage from tk.Tk , you cannot use row/column keyword arguments for grid in that case.
But I believe that you really did not intend it to be a subclass of tk.Tk , since you seem to be trying to initialize tk.Frame inside it. I believe the fix in your case would be to inherit the class from tk.Frame instead as -
class StartPage(tk.Frame):
Related
I have a question about why my child class can't see parents method(it can't see them, but it can use them). I'm creating a GUI in tkinter and I need to use some methods from my parent window.
My parent class look like this:
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
manager_of_action = Action_manager()
self.frames = {}
for F in (MenuView, LoginView, GameView, ResultView ):
page_name = F.__name__
frame = F(parent=container, controller=self,action_manager= manager_of_action)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("MenuView")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
and child class look like this:
class MenuView(tk.Frame):
def __init__(self, parent, controller, action_manager):
tk.Frame.__init__(self, parent)
self.controller = controller
button1 = tk.Button(self, text="Multiplayer",command=lambda: controller.show_frame("LoginView"),height=3,width=6, bg=("white"), font=controller.title_font)
button2 = tk.Button(self, text="Exit",command=lambda: exit(0),height=3,width=6,bg=("white"), font=controller.title_font )
button1.pack(side="top", fill="both", expand=True)
button2.pack(side="top", fill="both", expand=True)
But VS Code can't see .title_font() and .show_frame() but if I run my program, it is running as it should. Why it's happening and can I somehow fix it? (If I would have 50 methods in my parent class, I don't want to always look back to parent class and copy + paste my desired fuction.)
Thanks a lot for every help.
EDIT:
VScode doesn't highlight .title_font() and .show_frame() as it can be seen on picture. This means that VS code is not showing them in IntelliSense (Pylance) menu (. + space menu that will pop up after calling some object). Normally, functions are highlighted in yellow.
no highlight for .show_frame, .title_font
This is the window provides the container and methods which allow frame swapping:
class Login_Window(ctk.CTk):
def __init__(self, *args, **kwargs):
super().__init__()
self.geometry('400x400')
self.title('Music Mayhem')
self.resizable(False, False)
container = ctk.CTkFrame(master=self)
container.pack(side='top', fill='both', expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (LoginFrame, RegEmailFrame):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky= 'nsew')
frame.grid_columnconfigure(0,weight=1)
frame.grid_rowconfigure(0,weight=1)
self.show_frame(LoginFrame)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
In order to swap the frames, a button has to be created in the frame that is going to be swapped. How would i go about creating an instance of another class within these frames which will also call the show_frame method? Here is the code for the frames- this could be ran as long as you have tkinter and custom tkinter installed. The only aspect that should supposedly not work are the buttons in the menu frame.
Yes, in this situation the menu frame is not needed but this is just a simple example because the actual code is way too long to be included here.
I have tried adding the menu frame into the list of frames to be swapped (in the class above) and giving it the same parent and controller attributes as the other frame but that required a parent and controller argument to be passed through when it is called in the Login and Register frames.
Is there a way to get round this or a simpler method that could be implemented instead?
class LoginFrame (tk.Frame):
def __init__(self,parent, controller):
tk.Frame.__init__(self, parent)
self.menu = Menu(self)
self.menu.grid(row=0, column=0)
self.loginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(RegEmailFrame)
self.loginBtn.grid(row=1, column=0)
class RegEmailFrame(tk.Frame):
def __init__(self, parent, controller,header_name="Register Email"):
tk.Frame.__init__(self, parent)
self.menu = Menu(self)
self.menu.grid(row=0, column=0)
self.emailLabel = ctk.CtKLabel(master=self,width=100, height=20 text='Frame swapped')
self.emailLabel.grid(row=1, column=0)
class Menu(tk.Frame):
def __init__(self, *args, header_name="Logo Frame",
width=175, height=175,**kwargs):
super().__init__(*args, width=width, height=height, **kwargs)
self.menuloginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(LoginFrame)
self.menuloginBtn.grid(row=0, column=0)
self.menuRegBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(RegEmailFrame)
self.menuRegBtn.grid(row=1, column=0)
In the current implementation, the Menu class does not have access to the controller object that is used to switch between frames in the Login_Window class. One way to fix this would be to pass the controller object to the Menu class during instantiation.
You can do this by adding a parameter called controller in the Menu class constructor and then passing it as an argument when creating an instance of the Menu class in the LoginFrame and RegEmailFrame classes.
For example, in the LoginFrame class:
def __init__(self,parent, controller):
tk.Frame.__init__(self, parent)
self.menu = Menu(self, controller)
self.menu.grid(row=0, column=0)
And in the Menu class constructor:
def __init__(self, parent, controller, *args, header_name="Logo Frame",
width=175, height=175,**kwargs):
super().__init__(parent, *args, width=width, height=height, **kwargs)
self.controller = controller
With this changes, the Menu class now has access to the controller object and can use it to switch between frames using the show_frame method.
You should also make the same changes in the RegEmailFrame class and in the constructor of the Menu class.
Hope this helps!
I'm getting the error TypeError: __init__() takes from 1 to 3 positional arguments but 4 were given but i have no idea why??
I have one window which then calls a function to open a second window. This second window uses classes and throws up the error. Here are the lines of code that cause the error:
def openadmin():
class SCapp(tk.Toplevel(window)):
def __init__(self, *args, **kwargs):
tk.Toplevel.__init__(self, *args, **kwargs)
(Obviously that's not all the code, just the part which throws up the TypeError)
("window" is the first window which calls the function openadmin() to open the second window)
Any help would be appreciated because this is my coursework!
Edit:
IGNORE PREV CODE ^^
Here is a basic version of working code for the second window which uses frames (I applied some changes already):
import tkinter as tk
class SCapp(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 = {}
for F in (StartPage,): #list of frames
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label1 = tk.Label(self,text='This is the start page of 2nd window')
label1.pack()
def openadmin():
if __name__ == "__main__":
app = SCapp()
app.title("SC App")
app.attributes("-topmost", True)
app.mainloop()
And here is the first window (in a separate file) which is supposed to open the previous window:
import tkinter as tk
from testcode import *
def onClick():
if False:
return #some code is here
else:
openadmin()
def WindowOne():
global entid, entpassword
window = tk.Tk()
window.wm_title("Window 1")
label1 = tk.Label(text='This is the first window')
label1.pack()
enter = tk.Button(window, text = 'Click', command = onClick)
enter.pack()
window.mainloop()
WindowOne()
When these are in the same file it works fine, but when they are in separate files (like i need them to be) nothing happens when the button is clicked.
I'm currently working on a tkinter project. The code is shown below:
import tkinter as tk
from tkinter import font as tkfont
background_colour = "#F0F0F0"
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("CCNA Command Learner")
self.geometry("500x500")
self.iconbitmap("favicon.ico")
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 = {}
for F in (IntroPage, MainPage):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("IntroPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class IntroPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.image = tk.PhotoImage(file="IntroImage.gif")
self.head_img = tk.Label(self, image=self.image, background=background_colour)
self.head_img.pack()
self.label = tk.Label(self, bg=background_colour,
text="\nLearn essential commands for the CCNA exam\n\nPlease enter your name")
self.label.pack()
self.head_entry = tk.Entry(self)
self.head_entry.pack()
self.submit = tk.Button(self, text="Submit", command=self.submit_button)
self.submit.pack()
def submit_button(self):
global var_username
self.controller.show_frame("MainPage")
var_username = self.head_entry.get()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.introduction = tk.Label(self, text="Hello" + var_username)
self.introduction.pack()
if __name__ == "__main__":
app = Application()
app.mainloop()
My problem is that in the submit_button method of my IntroPage class, I am trying to set a global variable so that my MainPage class can then access it. However, despite declaring it and setting it, I am still getting a NameError when accessing it in my MainPage class?
That's correct, as you seem to use global incorrectly. It can make existing variable out of your scope visible and available for modification, like this:
a = 'test'
def f():
global a
a = 'edited'
f()
print(a)
Output:
>>> edited
You don't seem to declare variable with this name, hence the NameError. And again - global is not for creating global variables, it is for making them visible.
By the way, think twice before using globals in python, that usually indicates huge design problems. It's surely possible to refactor your code in a way that you won't need global.
You simply need create it before MainPage in call stack.
Create it outside the IntroPage class, and initializing can stay where it is.
I am trying to access the Text widget defined in class FirstPage from outside of the class.
I tried to solve this problem by creating a new instance of FirstPage, but could not find the right arguments to use. Also tried to use instance of GUI to gain the access, but unsuccessfully.
My problem is solved when I can use text.insert(0.0, t) from outside of the classes. It would help me modify the text displayed with Tkinter by functions that are not directly related with the GUI.
The origin of the code I am trying to use is found: Switch between two frames in tkinter
Also I removed lines that were not necessary for this question..
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
text = tk.Text(self , height=25, width=80)
text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.mainloop()
EDIT:
Here is the working code:
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
page_name = FirstPage.__name__
self.frames[page_name] = frame
def get_page(self, page_name):
return self.frames[page_name]
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.text = tk.Text(self , height=25, width=80)
self.text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")
app.mainloop()
There's nothing special you need to do. As with any python object, you simply need a reference to the object in order to manipulate it.
The concept in the code you started with is to have a "controller" that controls access to all of the pages, since that object is where the pages are created. You can add a function in the controller that gives you a reference to a page, and then you can use that to call a function on that page.
Here's the changes you need to make to the controller:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
...
page_name = FirstPage.__name__
self.frames[page_name] = frame
...
def get_page(self, page_name):
return self.frames[page_name]
You also need to modify FirstPage to keep a reference to the widget so that you can access it later:
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
...
self.text = tk.Text(...)
...
From within any other code you can now access the text widget via get_page (but your pages must save a reference to the controller for this to work).
class AnotherPage(tk.Frame):
def __init__(self, parent, controller):
...
self.controller = controller
...
def some_function(self):
...
first_page = self.controller.get_page("FirstPage")
text = first_page.text.get("1.0", "end-1c")
...
first_page.text.insert("end", "some new text\n")
Note that this technique works outside of any GUI pages. In your code, app is the controller, so you can do something like this:
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")