Getting the error "AttributeError: 'MainPage' object has no attribute 'deftext1'". Am I not defining 'deftext1' when I initially create it?
I should add that I am a beginner, so apologies if something (or all of it) looks a mess.
import tkinter as tk
class example(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Example")
tk.Tk.wm_geometry(self, '800x600')
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 = MainPage(container, self)
self.frames[MainPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
deftext1 = tk.Label(self, text="Text to change", font=("Arial Bold", 20))
deftext1.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Load",
command=lambda: self.updateLists())
button1.pack(pady=10,padx=10)
def updateLists(self):
self.deftext1.config(text='It Worked')
app=example() app.mainloop()
The variable deftext1 is a local variable, it is not the attribute of the class
Edit deftext1 to self.deftext1 so that it becomes an attribute of the class.
Your Code:
import tkinter as tk
class example(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Example")
tk.Tk.wm_geometry(self, '800x600')
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 = MainPage(container, self)
self.frames[MainPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.deftext1 = tk.Label(self, text="Text to change", font=("Arial Bold", 20))
self.deftext1.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Load",
command=lambda: self.updateLists())
button1.pack(pady=10,padx=10)
def updateLists(self):
self.deftext1.config(text='It Worked')
app=example()
app.mainloop()
I HOPE it worked.
Related
So I have my Tkinter application that consist of multiple frame
All these multiple frames contain the same basic structure of many buttons; the only difference is that the buttons have a different bg on each page.
In my actual project, these buttons contain so many options, and so having to write the same basic code each time for all pages makes my code look unnecessarily long.
So I'm thinking: Is there a way to put all these buttons into a dictionary or list, and pack them onto each separate frame? (Bear in mind the button will need to inherit the bg variable from the specific frame.)
I've created a minimal example to illustrate what I mean:
import tkinter as tk
from tkinter import *
listt = []
self = None
bg_colour_for_this_frame = None
button1 = Button(self,text="Button 1",bg=bg_colour_for_this_frame,fg='white')
button2 = Button(self,text="Button 2",bg=bg_colour_for_this_frame,fg='blue')
button3 = Button(self,text="Button 3",bg=bg_colour_for_this_frame,fg='orange')
listt.append(button1)
listt.append(button2)
listt.append(button3)
class Tkinter(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, SecondPage):
frame = F(container, self)
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()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self,text='SecondPage',command=lambda:controller.show_frame(SecondPage)).pack()
for s in listt:
s.pack()
class SecondPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self,text='StartPage',command=lambda:controller.show_frame(StartPage)).pack()
for s in listt:
s.pack()
app = Tkinter()
app.mainloop()
Or maybe, instead of having a list, use a dictionary:
listt = {'button1':'Button[root,text="Button 1",bg=bg_colour_for_this_frame,fg="white"]',
'button2':'Button[root,text="Button 2",bg=bg_colour_for_this_frame,fg="red"]',
'button3':'Button[root,text="Button 3",bg=bg_colour_for_this_frame,fg="blue"]',
}
I get the error:
s.pack()
AttributeError: 'str' object has no attribute 'pack'
Since you can't create the Buttons before the page they're on exists, It would be simpler to make a function and call it during the initialization of each of the page classes — like the make_buttons() shown below:
import tkinter as tk
from tkinter import *
# Button options for all pages.
BTN_OPTS = [dict(text="Button 1", fg='white'),
dict(text="Button 2", fg='blue'),
dict(text="Button 3", fg='orange')]
def make_buttons(parent, bg_colour):
return [Button(parent, bg=bg_colour, **opts) for opts in BTN_OPTS]
class Tkinter(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, SecondPage):
frame = F(container, self)
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()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self, text='SecondPage',
command=lambda: controller.show_frame(SecondPage)).pack()
for btn in make_buttons(self, 'Pink'):
btn.pack()
class SecondPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Button(self, text='StartPage',
command=lambda: controller.show_frame(StartPage)).pack()
for btn in make_buttons(self, 'green'):
btn.pack()
app = Tkinter()
app.mainloop()
A more sophisticated and object-oriented approach would be to define a base class for all page classes that had a method in it something like the function above, and then derive the concrete subclasses from that allowing them just inherit the method. It also gets rid of the global data because the button options are now in a (base) class attribute.
Here's a runnable example of how it could be done that way. Note: it requires Python 3.6+ because it uses object.__init_subclass__() which was added in that version:
import tkinter as tk
class Tkinter(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, SecondPage):
frame = F(container, self)
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()
frame.winfo_toplevel().geometry("860x864")
frame.configure(bg='#000000')
class BasePage(tk.Frame):
# Button options common to all pages.
BTN_OPTS = [dict(text="Button 1", fg='white'),
dict(text="Button 2", fg='blue'),
dict(text="Button 3", fg='orange')]
#classmethod
def __init_subclass__(cls, /, bg_color, **kwargs):
super().__init_subclass__(**kwargs)
cls.bg_color = bg_color
def __init__(self, parent, controller, text, command):
super().__init__(parent)
tk.Button(self, text=text, command=command).pack() # Next page button.
for btn in (tk.Button(self, bg=self.bg_color, **opts) for opts in self.BTN_OPTS):
btn.pack()
class StartPage(BasePage, bg_color='pink'):
def __init__(self, parent, controller):
super().__init__(parent, controller, text='SecondPage',
command=lambda: controller.show_frame(SecondPage))
class SecondPage(BasePage, bg_color='green'):
def __init__(self, parent, controller):
super().__init__(parent, controller, text='StartPage',
command=lambda: controller.show_frame(StartPage))
app = Tkinter()
app.mainloop()
I have two frames to switch around. In each frame, there is an Entry box. How do I set my input in the first Entry box in the first frame as the default text for the second Entry box in the second frame?
from tkinter import *
class Root(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = 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 (PageOne, PageTwo):
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("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def quit(self):
self.destroy()
text = ''
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
global text
entry_box_1 = Entry(self, width=40)
entry_box_1.pack()
text = entry_box_1.get()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame('PageTwo'))
next_button.pack()
quit_button.pack()
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
entry_box_2 = Entry(self, width=40)
entry_box_2.insert(END, text)
entry_box_2.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
back_button.pack()
quit_button.pack()
if __name__ == "__main__":
root = Root()
root.mainloop()
My second Entry box keeps being blank when I switch to the second frame.
You are initializing the PageTwo frame when text variable is an empty string and while you are switching to this frame you are not inserting any value to entry_box_2.
I suggest you to invoke the insert method every time you are switching to the PageTwo frame. You can do this by creating a new switching method that can be similar to show_frame_with_default_text method in the following code.
from tkinter import *
class Root(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = 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 (PageOne, PageTwo):
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("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def show_frame_with_default_text(self, page_name, text):
frame = self.frames[page_name]
frame.entry_box.delete(0, END)
frame.entry_box.insert(0, text)
frame.tkraise()
def quit(self):
self.destroy()
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
entry_box_1 = Entry(self, width=40)
entry_box_1.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame_with_default_text('PageTwo', entry_box_1.get()))
next_button.pack()
quit_button.pack()
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
self.entry_box = Entry(self, width=40)
self.entry_box.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
back_button.pack()
quit_button.pack()
if __name__ == "__main__":
root = Root()
root.mainloop()
I have a problem with my code:
I want that the OK-button (tkinter) in a popup-window (exp_Name) makes three things when it gets pressed:
First it should store a string from the Entry (in the popup-window) in a global variable
exp_name = entry.get()
Second it should open another window
app.show_frame(PageOne)
Third it should close the popup-window
popup.destroy
I tried to call a function, but I have no idea how I should close the popup window in a separate function. And also how I should store the Entry in a global variable in this separate function.
Thanks a lot!!
Here the whole code:
import tkinter as tk
from tkinter import ttk
global exp_name
exp_name = "Hello"
def exp_Name():
popup = tk.Tk()
popup.geometry("800x400")
label=ttk.Label(popup, text="Enter Something:")
label.pack(side="top", fill="x", pady=10)
entry = ttk.Entry(popup)
entry.pack()
##Here is the problem...
OK_btn=ttk.Button(popup, text="OK", command= lambda: [app.show_frame(PageOne),exp_name = entry.get(), popup.destroy])
OK_btn.pack()
Close_btn=ttk.Button(popup, text="Close",command = popup.destroy)
Close_btn.pack()
popup.mainloop()
class Steppermovementsapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default="images/Icon_fly.ico")
tk.Tk.wm_title(self, "Drosophila ADF apparatus")
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, PageOne):
frame = F(container, self)
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, controller):
tk.Frame.__init__(self, parent)
exp_btn = ttk.Button(self, text="Start experiment",command=exp_Name)
exp_btn.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label =ttk.Label(self, text=exp_name)
label.pack()
app = Steppermovementsapp()
app.geometry("{0}x{1}".format(app.winfo_screenwidth(),app.winfo_screenheight()))
app.mainloop()
This is how I tried it with a function:
import tkinter as tk
from tkinter import ttk
global exp_name
exp_name = "Hello"
class sidefunctions(tk.Tk):
def func_three(self):
app.show_frame(PageOne)
exp_name = self.entry.get()
popup.destroy
def exp_Name(self):
self.popup = tk.Tk()
self.popup.geometry("800x400")
self.label=ttk.Label(self,text="Enter Something:")
self.label.pack(side="top", fill="x", pady=10)
self.entry = ttk.Entry(self)
self.entry.pack()
self.OK_btn=ttk.Button(self,text="OK", command= self.func_three)
self.OK_btn.pack()
self.Close_btn=ttk.Button(self,text="Close",command =
self.popup.destroy)
self.Close_btn.pack()
self.popup.mainloop()
class Steppermovementsapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self, default="images/Icon_fly.ico")
tk.Tk.wm_title(self, "Drosophila ADF apparatus")
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, PageOne):
frame = F(container, self)
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, controller):
tk.Frame.__init__(self, parent)
exp_btn = ttk.Button(self, text="Start experiment",command=sidefunctions().exp_Name)
exp_btn.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label =ttk.Label(self, text=exp_name)
label.pack()
app = Steppermovementsapp()
app.geometry("{0}x{1}".format(app.winfo_screenwidth(),app.winfo_screenheight()))
app.mainloop()
Here is an example of my code
import tkinter as tk
from tkinter import ttk
class gui_programming(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="left", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, Page1):
frame = F(container, self)
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()
##number 1
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
c = tk.Canvas(self, bg="red", width=75, height=100)
c.place(x=0, y=0)
butt0 = ttk.Button(self, text="Next.", command=lambda:controller.show_frame(Page1))
controller.bind("1", lambda x: controller.show_frame(StartPage))
controller.bind("2", lambda x: controller.show_frame(Page1))
controller.bind("3", lambda x: controller.show_frame(Page2))
class Page1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
lambda x: gui_programming().geometry("75x100+10+10")
c = tk.Canvas(self, bg="blue", width=300, height=200)
c.place(x=0, y=0)
app = gui_programming()
app.geometry("75x100+10+10")
app.mainloop()
So, how should I go about changing the window that each class appears in from 75x100 to something else, is there a command to resize the window? I would like to be able to resize it for each class.
You already resized the app window from its default with
app.geometry("75x100+10+10")
Repeat this with a different size.
I recommend to always use variables in geometry
e.g
w=500
h=500
master.geometry(('{}x{}').format(w,h))
Call geometry inside __init__ method:
class gui_programming(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.geometry("75x100+10+10")
How can i call a another frame that i initialized with a statenent?
Here is my code:
class MindQuiz(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 (LogIn, Register, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(LogIn)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class LogIn(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.errorcount=0
self.usernameEntry = tk.Entry(self,width=20,font=('Segoe Ui',15))
self.usernameEntry.place(x=200,y=300)
self.passwordEntry = tk.Entry(self,width=20,font=('Segoe Ui',15),show='*')
self.passwordEntry.place(x=200,y=400)
button = ttk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(Register))
button.place(x=100,y=100)
button2 = ttk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.place(x=150,y=150)
submit = ttk.Button(self, text="Log In",width=10,
command= self.CheckAccount)
submit.place(x=350,y=150)
def CheckAccount(self):
f= open('account.txt','r+')
found = 0
username = self.usernameEntry.get()
password = self.passwordEntry.get()
for line in f:
str=line.split("~")
"*".join(str)
if(username==str[0] and password==str[1]):
tk.messagebox.showinfo(title="Success", message="Welcome!\n Successfully Logged In!" )
found=1;
if(found==1):
break;
if(found==0):
self.errorcount +=1;
tk.messagebox.showinfo(title="Error", message="Invalid!\n Incorrect Username Or Password!")
if(self.errorcount>=3):
sys.exit()
print(self.errorcount);
if(found==1):
self.controller.show_frame(Register) ##<-------------------
You need to initialize the controller in the __init__like this:
class LogIn(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.controller = controller
self.errorcount=0
Now your code will work.