how to switch from pack() to place() properly - python-3.x

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.

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

Tkinter button "clickbox" detached from widget in fullscreen

I am on MacOS 10.15.7 and have just found that when I run my application, the buttons work perfectly fine no matter how large I make the window. However, when I make the window go into fullscreen mode, the buttons register clicks inside a box about 20 pixels below where the widget shows. Has anyone else come across this issue? I'm using the grid geometry manager, with sticky="we" so I don't think that the way I've added the buttons is the problem. I have also called grid_rowconfigure(i, weight=1) on every row of my application to perform the appropriate vertical alignment when stretching.
Here is a small snippet that shows how I've structured the application.
import tkinter as tk, tkinter.ttk as ttk
class Application(tk.Tk):
def __init__(self):
super().__init__()
container = ttk.Frame(self)
container.pack(fill='both', expand=True)
self.frames = {}
for frame in (Dashboard, PageOne, PageTwo, PageThree):
f = frame(container, self)
self.frames[frame] = f
f.grid(column=0, row=0, sticky="nswe")
self.switch_page(Dashboard)
def switch_page(self, frame):
f = self.frames[frame]
f.tkraise()
class Dashboard(ttk.Frame):
def __init__(self, master, controller):
super().__init__(master)
self.master = master
button1 = ttk.Button(self, text="Page One",
command=lambda: controller.switch_page(PageOne))
button1.grid(column=1, row=0, sticky="we")
button2 = ttk.Button(self, text="Page Two",
command=lambda: controller.switch_page(PageTwo))
button2.grid(column=1, row=1, sticky="we")
button3 = ttk.Button(self, text="Page Three",
command=lambda: controllerswitch_page(PageThree))
button3.grid(column=1, row=2, sticky="we")
ttk.Label(self).grid(column=0, row=0, rowspan=3)
ttk.Label(self).grid(column=2, row=0, rowspan=3)
class PageOne(ttk.Frame):
def __init__(self, master, controller):
super().__init__(master)
self.master = master
ttk.Button(self, text="Back to home",
command=lambda: controller.switch_page(Dashboard)
).pack()
class PageTwo(ttk.Frame):
def __init__(self, master, controller):
super().__init__(master)
self.master = master
ttk.Button(self, text="Back to home",
command=lambda: controller.switch_page(Dashboard)
).pack()
class PageThree(ttk.Frame):
def __init__(self, master, controller):
super().__init__(master)
self.master = master
ttk.Button(self, text="Back to home",
command=lambda: controller.switch_page(Dashboard)
).pack()
Also, I realized that I commented out the calls to grid.row_configure so I don't believe that is contributing to the issue. Again, the problem is not happening with the application in general, only when I click the fullscreen button on the window itself.

How to access a button inside of a class in tkinter

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()

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()

switch frame with tkinter

I use following code:
Switch between two frames in tkinter
import RPi.GPIO as GPIO
from threading import Timer,Thread,Event
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
#----------------------------------------------------------
#Setup GPIO
#----------------------------------------------------------
GPIO.setwarnings(False)
# RPi.GPIO Layout verwenden (wie Pin-Nummern)
#GPIO.setmode(GPIO.BOARD)
GPIO.setmode(GPIO.BCM)
# (GPIO 17,27,22,5) auf Input setzen
chan_list1 = [17, 27, 22, 5]
GPIO.setup(chan_list1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# (GPIO 12,13,23,24) auf Output setzen
chan_list = [12, 13, 23, 24]
GPIO.setup(chan_list, GPIO.OUT)
#----------------------------------------------------------
#--------------------------------------------------------
#Timer
#--------------------------------------------------------
class perpetualTimer():
def __init__(self,t,hFunction):
self.t=t
self.hFunction = hFunction
self.thread = Timer(self.t,self.handle_function)
def handle_function(self):
self.hFunction()
self.thread = Timer(self.t,self.handle_function)
self.thread.start()
def start(self):
self.thread.start()
def cancel(self):
self.thread.cancel()
#--------------------------------------------------------
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.overrideredirect(1) #Remove Title bar
self.geometry("480x272") #Set the window dimensions
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)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
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("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given 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
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
def printer():
print ('PIT(!)')
SampleApp.show_frame('PageOne')
if __name__ == "__main__":
t = perpetualTimer(0.01,printer)
t.start()
app = SampleApp()
app.mainloop()
Is it possible to switch the frames without using buttons?
e.g. comparing a counter. if the counter value is 1 show page 1
if counter value is 2 show page 2 and so on.
If I'm honest, I do not remember exactly what I've all tried.
One thing I tried was "controller.show_frame("StartPage")" from def printer sub.
What I want to achieve is to switch the pages from the "def printer" sub,
which is called periodically.
I do not know if that's possible?
One thing I tried was controller.show_frame("StartPage") from def printer sub.
You have to call switch_frame on the object that has that method. This is no different than any other object or any other class in python.
In your case, since SampleApp is the class that has the method, and app is a global variable that holds an instance of the class, you would do it this way:
app.show_frame("StartPage")

Resources