How to access a button inside of a class in tkinter - python-3.x

I'm using Python 3.8.0 and Tkinter 8.6.
I'm trying to access button one through the button.config method but I don't know how to access the button outside the class.
I tried assigning a name to button one and then trying app.name.config() but it didn't work and Python didn't recognize the name.
import tkinter as tk
from tkinter import font
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.theWidgets()
def theWidgets(self):
self.one = tk.Button(self, text='New Game',command=onePressed())
self.one.grid(row=0,column=0,padx=100)
self.two = tk.Button(self, text='Load Game')
self.two.grid(row=1,column=0,padx=100,pady=10)
self.three = tk.Button(self, text='Quit', command=self.quit,anchor=tk.W,font='Helvetica 18 bold')
self.three.grid(row=2, column=0, padx=100, pady=10)
app = Application()
def onePressed():
#change state of button one to tk.DISABLED
app.mainloop(
)

Code that controls the internal state of a class member (e.g. a button) normally belongs inside the class.
The actions of a button press should be defined as part of your class.
(As with anything, there are occasionally exceptions but for now follow the above).
For more details, see en.wikipedia.org/wiki/Encapsulation_(computer_programming).
All you need to do in your example is move the button press function inside your class and then associate it with your button (in the same way you associated quit with button 3).
import tkinter as tk
from tkinter import font
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.theWidgets()
def theWidgets(self):
self.one = tk.Button(self, text='New Game', command=self.onePressed)
self.one.grid(row=0,column=0,padx=100)
self.two = tk.Button(self, text='Load Game')
self.two.grid(row=1,column=0,padx=100,pady=10)
self.three = tk.Button(self, text='Quit', command=self.quit, anchor=tk.W, font='Helvetica 18 bold')
self.three.grid(row=2, column=0, padx=100, pady=10)
def onePressed(self):
self.one.config(state="disabled")
app = Application()
app.mainloop()

Related

Trouble Binding a Button in a Class - Python (Tkinter)

Just started learning Tkinter and was hoping someone could help me. I've been trying to bind a keyboard character (Enter button) to a tk button following this example and not getting anywhere.
Say I take the button (Enter) and try bind it nothing happens:
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
If I bind to self instead using Lambda nothing happens also. I can get it to trigger if I remove the lambda but that's not the desired outcome
self.bind('<Return>', lambda:self.retrieve_Input(t))
My Code:
import sys
import tkinter as tk
from tkinter import ttk
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Test Application")
self.lift() #Bringing the GUI to the front of the screen
main_frame = tk.Frame(self, height=400, width=600) #Creating a main Frame for all pages
main_frame.pack(side="top", fill="both", expand=True)
main_frame.grid_rowconfigure(0, weight=1) #Configuring the location of the main frame using grid
main_frame.grid_columnconfigure(0, weight=1)
# We will now create a dictionary of frames
self.frames = {}
for F in (MainPage, CompletionScreen): #Add the page components to the dictionary.
page = F(main_frame, self)
self.frames[F] = page #The windows class acts as the root window for the frames.
page.grid(row=0, column=0, sticky="nsew")
self.show_page(MainPage) #Method to switch Pages
def show_page(self, cont):
frame = self.frames[cont]
frame.tkraise()
##########################################################################
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#switch_window_button = tk.Button(self, text="Go to the Side Page", command=lambda: controller.show_page(SidePage))
#switch_window_button.pack(side="bottom", fill=tk.X)
tk.Label(self, text="Project Python Search Engine", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
tk.Label(self, text="Song", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
t = tk.Entry(self, bg='white', width = 50)
t.pack()
tk.Label(self, text="", bg='white').pack()
Enter = tk.Button(self, text='Search', command= lambda:self.retrieve_Input(t))
Enter.pack()
tk.Button(self, text="Latest Popular Songs", command=lambda:self.Popular_Songs(t)).pack() #Line 210 onwards
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
def retrieve_Input(self, t):
print ("work")
print (t)
class CompletionScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Completion Screen, we did it!")
label.pack(padx=10, pady=10)
switch_window_button = ttk.Button(
self, text="Return to menu", command=lambda: controller.show_page(MainPage)
)
switch_window_button.pack(side="bottom", fill=tk.X)
if __name__ == "__main__":
App = windows()
App.mainloop()
I'm not really sure what I'm missing
Answer: The button probably doesn't have the keyboard focus. When I run your code and then use the keyboard to move the focus to the button, your binding works. You probably want to bind to the entry widget rather than the button since that's what will have the keyboard focus. – Thanks Bryan Oakley

Passing variables between frames in Tkinter 2.0?

Passing variables between frames in Tkinter
Followed the multiframe class example in:
from Switch between two frames in tkinter
by Bryan Oakley / Steven M. Vascellaro
Everything works fine within each frame, but trying to pass a Frame StartPage-entry text into the Frame PageTwo-scroll requires syntax in :
txtScroll(txt): ??????scroll.insert(tk.INSERT,scrolltxt)
I do not get.
BTW:
Tried following the original solution to:
Passing variables between frames in Tkinter
by adding
def __init__(self, master,controller, uploadPage=None):
self.controller = controller
self.uploadPage = PageTwo
To no avail ??????
# from https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter
# by Bryan Oakley / Steven M. Vascellaro
import tkinter as tk # python 3
from tkinter import ttk, font as tkfont, scrolledtext # python 3
from tkinter.ttk import *
#definitions ----------------------
def txtClicked(txt):
res = "Entry text " + txt.get()
print(res)
def txtScroll(txt):
# Problem with output to scroll in different class ?
scroll.insert(tk.INSERT,scrolltxt)
def scrollInsert(scroll,scrolltxt):
scroll.insert(tk.INSERT,scrolltxt)
#Frame class ----------------------
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.replace_frame(StartPage)
def replace_frame(self, frame_class):
"""Destroys current frame and replaces i with a new one."""
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.pack()
class StartPage(tk.Frame):
def __init__(self, master, uploadPage=None):
tk.Frame.__init__(self, master)
#self.controller = controller
self.uploadPage = PageTwo
tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10)
# Entry
txt = Entry(self,width=10)
txt.pack(side="top", fill="x", pady=10)
btn = Button(self, text="Get Entry", command=lambda: txtClicked(txt)).pack()
btn = Button(self, text="Scroll Output", command=lambda: txtScroll(txt)).pack()
#-------------
tk.Button(self, text="Open page one", command=lambda: master.replace_frame(PageOne)).pack()
tk.Button(self, text="Open page two", command=lambda: master.replace_frame(PageTwo)).pack()
class PageOne(tk.Frame):
def __init__(self, master, uploadPage=None):
tk.Frame.__init__(self, master)
#self.controller = controller
self.uploadPage = PageTwo
tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page", command=lambda: master.replace_frame(StartPage)).pack()
class PageTwo(tk.Frame):
def __init__(self, master, uploadPage=None):
tk.Frame.__init__(self, master)
#self.controller = controller
print ("tk.Frame: ", tk.Frame )
print ("self: ", self )
print ("master: ", master )
# scrolledtext
scroll = scrolledtext.ScrolledText(self,width=40,height=10)
scroll.pack(side="bottom", padx=10, pady=10)
scrolltxt = "Print scroll stuff..."
btn = Button(self, text="Print scroll", command=lambda: scrollInsert(scroll,scrolltxt)).pack()
#-------------
tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10)
tk.Button(self, text="Return to start page", command=lambda: master.replace_frame(StartPage)).pack()
#Tkinder Main ----------------------
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

how to switch from pack() to place() properly

i have my tkinter gui split in different classes and want to connect them in a home screen with buttons, like Steven M. Vascellaro did here: Switch between two frames in tkinter
import tkinter.ttk as ttk
#from alexa_site_2 import Frame_alexa
from globals import Globals
from tkinter import PhotoImage
import tkinter as tk
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self._frame = None
self.geometry('{}x{}'.format(1024, 600))
self.configure(bg="white")
for i in range(len(Globals.image_files)):
image_box = PhotoImage(file=Globals.image_files[i])
Globals.list_images.append(image_box)
self.switch_frame(StartPage)
def switch_frame(self, frame_class):
"""Destroys current frame and replaces it with a new one."""
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.place(x=110,y=0)
print("switch frame function")
class StartPage(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
#self.place(x=0,y=0)
ttk.Label(self, text="This is the start page").pack()
ttk.Button(self, text="Open page one",
command=lambda: master.switch_frame(PageOne)).pack()
ttk.Button(self, text="Open page two",
command=lambda: master.switch_frame(PageTwo)).pack()
ttk.Button(self, text="Open page two",
command=lambda: master.switch_frame(Frame_alexa)).pack()
class PageOne(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
ttk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)
ttk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(StartPage)).pack()
class PageTwo(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
ttk.Label(self, text="This is page two").place(x=40,y=10)
ttk.Button(self, text="Return to start page",
command=lambda: master.switch_frame(StartPage)).place(x=0,y=0)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
when I try connect my own classes i get a white window as result. You can see it working in pageOne and my problem in PageTwo. I am not planning on mixing place() and pack(). I just need place() and your help please.

Python3 tkinter - New window with class is blank + new window contents in original window

I am dabbling in tkinter's possibilities to make a simple application that shows a "Enter password" little window upon startup. But the weirdest behaviour started to happen...
mainWindow.py
import tkinter as tk
import password
class mainWindow(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("mainWindow")
self.geometry("{0}x{1}+20+20".format(50,50))
if __name__ == "__main__":
mainW = mainWindow()
passW = password.passwordWindow()
passW.resizable(False, False)
passW.attributes("-topmost", True)
passW.mainloop()
password.py
import tkinter as tk
import mainWindow
class passwordWindow(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Password")
self.frame = tk.Frame(height=2, bd=1, relief=tk.SUNKEN)
self.frame.pack(fill=tk.X, padx=5, pady=5)
self.label = tk.Label(self.frame, text="This Label is packed\nin the Password's Frame.")
self.label.pack(fill=tk.BOTH, expand=1)
Result:
Needless to say, it's not the desired effect. The "Label" part should be on the password window! Any clue why am I getting this result? Thanks in advance!!
The 1st porblem I can see is you are using Tk() twice here. Instead of using Tk() for a new window use Toplevel() instead. Toplevel is meant to be used to create new windows after the main window has been generated.
Next we need to pass the root window to the password class so we can use it as the top level of the main windows instance.
So in short your code should look like this:
mainWindow.py
import tkinter as tk
import password
class mainWindow(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("mainWindow")
self.geometry("{0}x{1}+20+20".format(50,50))
if __name__ == "__main__":
mainW = mainWindow()
passW = password.passwordWindow(mainW)
passW.resizable(False, False)
passW.attributes("-topmost", True)
mainW.mainloop()
password.py
import tkinter as tk
import mainWindow
class passwordWindow(tk.Toplevel):
def __init__(self, controller):
tk.Toplevel.__init__(self, controller)
self.title("Password")
self.frame = tk.Frame(self, height=2, bd=1, relief=tk.SUNKEN)
self.frame.pack(fill=tk.X, padx=5, pady=5)
self.label = tk.Label(self, text="This Label is packed\nin the Password's Frame.")
self.label.pack(fill=tk.BOTH, expand=1)
Results:

Why doesn't the program wait for the function result? [duplicate]

I have a tkinter class:
class DBCreatorWin():
def closeWindow(self):
tkMessageBox.showinfo("Ilmiont SQLite Database Manager", "This window cannot be closed.\nEnter a database name and press Continue.")
def returnName(self):
dbName = self.entry.get()
self.window.destroy()
return dbName
def __init__(self):
self.window = Toplevel()
self.window.transient(tkRoot)
self.window.grab_set()
self.window.resizable(width=False, height=False)
self.window.title("Ilmiont SQLite Database Manager")
self.window.protocol("WM_DELETE_WINDOW", self.closeWindow)
self.label = Label(self.window, text="Enter the name of the database to be created: ")
self.entry = Entry(self.window, width=30)
self.button = Button(self.window, text="Continue", command=self.returnName)
self.label.grid(row=0, column=0)
self.entry.grid(row=0, column=1)
self.button.grid(row=1, column=0, columnspan=2)
I want to create an instance of this class within my main code and wait for the return value. The user types a name into the entry field and presses the Continue button. At that point, the value should be returned to where the class was originally instantiated. How do I go about this? I can't seem to make it work in a normal way and am new to tkinter.
Thanks in advance,
Ilmiont
There are a couple of ways to do this. The basic idea is to use a tkinter method to wait for a specific event before returning. Tkinter provides two methods to do just that: wait_window and wait_variable. The most common method is to open a window and then wait for it to be destroyed. Some good examples can be found on the effbot site, on a page titled Dialog Windows.
Here's a simple illustration. It's not production-ready, but illustrates the general idea. At the very least you'll want to add a grab on the dialog so that you can't interact with the main window while the dialog is open, since you said you want the dialog to be modal.
import Tkinter as tk
class MyDialog(object):
def __init__(self, parent):
self.toplevel = tk.Toplevel(parent)
self.var = tk.StringVar()
label = tk.Label(self.toplevel, text="Pick something:")
om = tk.OptionMenu(self.toplevel, self.var, "one", "two","three")
button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)
label.pack(side="top", fill="x")
om.pack(side="top", fill="x")
button.pack()
def show(self):
self.toplevel.deiconify()
self.toplevel.wait_window()
value = self.var.get()
return value
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.button = tk.Button(self, text="Click me!", command=self.on_click)
self.label = tk.Label(self, width=80)
self.label.pack(side="top", fill="x")
self.button.pack(pady=20)
def on_click(self):
result = MyDialog(self).show()
self.label.configure(text="your result: %s" % result)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
You can't.
The whole way the tkinter works is with callbacks. The command that you're using is the callback and you'll have to use the value inside the class. Here is an example:
def do_stuf(self):
tkMessageBox.showinfo("Foo", returnName())
....................
self.button = Button(self.window, text="Continue", command=self.do_stuff)

Resources