I am trying to animate a frame in Tkinter but I am unable to do so. When I wish to update the sliders I get a white screen which is unresponsive. I have followed other examples of doing a
self.after(1000, self.UpdateSliders)
and
app.mainloop()
but because I'm using frames and swapping them out when a button is clicked it won't refresh the frame.
How do I refresh the sliders to move on that frame?
Code for the swapping of the frames
# Setup and declaring pages
class SoundBoard(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
# Window Config
self.title("Sound Board")
self.geometry('975x500')
self.iconbitmap(FileDir + 'Images/Icons/Black Icon.ico')
#Setting Up frames for each window
container = Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#Create a blank dictionary which will be populated later
self.frames = {}
#Populate dictionary with all the pages (frame) in which the program uses
for F in (MainWindow,
ButtonsWindow,
SliderWindow,
DisplayWindow,
):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame.configure(bg='#1b191a')
# Show the Main window
self.show_frame(MainWindow)
# Moves the frame to the front
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
Code for my mainframe in which I want the sliders to update.
class MainWindow(Frame):
def __init__(self, parent, controller):
Frame.__init__(self,parent)
# Set up sliders
self.Slider1 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
self.Slider2 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
self.Slider3 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
self.Slider4 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
self.Slider5 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
self.Slider6 = Slider(self,100,60,200,15,15,35,0,80,"White","Black")
while True:
self.after(1000, self.UpdateSliders)
# Set value of the sliders
def UpdateSliders(self):
self.Slider1.SetVal(self.Board1, SavedInfo.ReadSliders("Slider1"))
self.Slider2.SetVal(self.Board1, SavedInfo.ReadSliders("Slider2"))
self.Slider3.SetVal(self.Board1, SavedInfo.ReadSliders("Slider3"))
self.Slider4.SetVal(self.Board1, SavedInfo.ReadSliders("Slider4"))
self.Slider5.SetVal(self.Board1, SavedInfo.ReadSliders("Slider5"))
self.Slider6.SetVal(self.Board1, SavedInfo.ReadSliders("Slider6"))
self.update()
print("S")
I write an example code for you with tabs change manuyal and automate. I hope will be usefull to learn form it.
import tkinter as tk
root = tk.Tk()
class Main:
def __init__(self, master):
#// Define frames (buttons // content)
self.frame_tabs = tk.Frame(master)
self.frame_content = tk.Frame(master)
#// Pack frames
self.frame_tabs.grid(row=0, column=0, sticky="nws")
self.frame_content.grid(row=0, column=1, sticky="nesw")
#// Define content frames
self.content_1 = tk.Frame(self.frame_content)
self.content_2 = tk.Frame(self.frame_content)
self.content_3 = tk.Frame(self.frame_content)
self.content_4 = tk.Frame(self.frame_content)
self.content_5 = tk.Frame(self.frame_content)
self.content_6 = tk.Frame(self.frame_content)
self.content_7 = tk.Frame(self.frame_content)
#// Pack content frames
self.Frame_Content = [self.content_1, self.content_2, self.content_3, self.content_4, self.content_5, self.content_6, self.content_7]
for content_f in self.Frame_Content:
content_f.grid(row=0, column=0, sticky="nesw")
#// Define tab buttons
self.tab_1 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_1, self.tab_1))
self.tab_2 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_2, self.tab_2))
self.tab_3 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_3, self.tab_3))
self.tab_4 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_4, self.tab_4))
self.tab_5 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_5, self.tab_5))
self.tab_6 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_6, self.tab_6))
self.tab_7 = tk.Button(self.frame_tabs, command=lambda:Switch_Content(self.content_7, self.tab_7))
#// Pack tab buttons
self.Tab_Button = [self.tab_1, self.tab_2, self.tab_3, self.tab_4, self.tab_5, self.tab_6, self.tab_7]
self.t_index = 0
for tab in self.Tab_Button:
# Generate text content
self.Test_Content(self.Frame_Content[self.t_index],
f"Hello my friend!\nThis is content from tab {self.t_index + 1} :)")
# Tab properties
tab.configure(bg="lightgray", activebackground="gray", bd=0,
font=("Calibri", 12, "bold"), text=f"Tab {self.t_index + 1}")
tab.grid(row=self.t_index, column=0, sticky="wne")
self.t_index += 1
#// Create Auto button for auto switch content
self.btn_auto = tk.Button(self.frame_tabs, text=f"Auto", font=("Calibri", 12, "bold"),
bg="lightgray", activebackground="gray", relief="groove",
command=lambda: Switch_Auto(this_btn=self.btn_auto, time=500))
#// Pack: self.t_index on last tab button was increased so will use as reference for this
#// in case you add other tabs this will be updated to
self.btn_auto.grid(row=self.t_index, column=0, sticky="wse")
def Switch_Content(frame, tab):
for button in self.Tab_Button:
# Make background light gray for all tabs buttons
button.configure(bg="lightgray")
# Change content frame and also change tab button background color
# like identifier to know on witch tab we are
frame.tkraise()
tab.configure(bg="tomato")
#// Activate Switch effects for first tab by default we se first tab and content
Switch_Content(self.content_1, self.tab_1)
self.auto_switch_on = False
def Switch_Auto(time, this_btn):
#// Logic to know when to start and stop animation
#// Basic si just switch form TRUE to FALSE and vice-versa
if self.auto_switch_on == False:
self.auto_switch_on = True
this_btn.configure(bg="green")
else:
self.auto_switch_on = False
this_btn.configure(bg="lightgray")
self.index = 0
#// If animation signal is true
while self.auto_switch_on == True:
#// If animation signal is false then stop animation
if self.auto_switch_on == False: break
#// Switch content
Switch_Content(self.Frame_Content[self.index], self.Tab_Button[self.index])
#// Upgrade index
self.index += 1
if self.index == len(self.Tab_Button): self.index = 0
#// master will be in this case root -> "tkinter.Tk()"
master.after(time)
master.update()
#// We create a simple label widget what will reuse it on tab loop pack (just for this example we will generate it)
def Test_Content(self, frame, text):
tk.Label(frame, text=text, font=("Comic Sans MS", 18, "bold italic")).grid(row=0, column=0, sticky="nesw")
if __name__ == "__main__":
Main(root)
root.mainloop()
I updated the code with some more comments and color change on Auto button on active state.
Related
For tkinter in python, I created a collapsible widget that shows/hides some content on click. I found an example on this site and adjusted it to my needs.
What I try to do is to put multiple collapsibles into a 3-column layout. The problem is that opening a collapsible in one column causes the other columns to "jump". This effect can be seen when running the example code below. It seems that if the content of the collapsible is longer than the length of both labels in the header of the collapsible (i. e. the text "Collapsed" and the "+" character) the colums begin to jump because the other one is getting expanded. I already experimented with settings like columnspan, sticky, etc. on all different levels of parent and child widgets, but nothing worked.
Does anyone have an idea on how to make the 3-column layout without the "jumping" effect? If there exists a totally different approach to reach the goal, I would be glad to get some hints!
Here is the example code:
import tkinter as tk
# Collapsible class
class Collapsible(tk.Frame):
def __init__(self, parent):
# Call __init__ of parent class
tk.Frame.__init__(self, parent)
# Setting weight to the first column makes the second column "float".
# This puts the "+" and "-" characters to the right of the column.
self.columnconfigure(index=0, weight=1)
# If value=0, collapsible is initially closed.
# If value=1, collapsible is initially opened.
self._variable = tk.IntVar(value=0)
# Frame for clickable header of collapsible
self.header_frame = tk.Frame(self, name='header_frame', bg='yellow')
self.header_frame.columnconfigure(1, weight=1)
self.header_frame.bind('<Button-1>', lambda e: self.toggle())
self.header_frame.grid(row=0, column=0, sticky='ew')
# Frame for content of collapsible
self.content_frame = tk.Frame(self, name='content_frame', bg='green')
# Call _activate method
self._activate()
def _activate(self):
# Remove all child widgets from header frame
for child in self.header_frame.winfo_children():
child.destroy()
# Closed: self._variable.get() = 0
if not self._variable.get():
# Labels in header frame if collapsed
collapsed_header_1 = tk.Label(self.header_frame, text='Collapsed', bg='purple')
collapsed_header_1.bind('<Button-1>', lambda e: self.toggle())
collapsed_header_1.grid(column=0, row=0, sticky='nw')
collapsed_header_2 = tk.Label(self.header_frame, text='+', width=2, bg='purple')
collapsed_header_2.bind('<Button-1>', lambda e: self.toggle())
collapsed_header_2.grid(column=1, row=0, sticky='ne')
# Forget the content frame if collapsible is closed
self.content_frame.grid_forget()
# Opened: self._variable.get() = 1
elif self._variable.get():
# Labels in header frame if expanded
expanded_header_1 = tk.Label(self.header_frame, text='Expanded', bg='purple')
expanded_header_1.bind('<Button-1>', lambda e: self.toggle())
expanded_header_1.grid(column=0, row=0, sticky='nw', )
expanded_header_2 = tk.Label(self.header_frame, text='-', width=2, bg='purple')
expanded_header_2.bind('<Button-1>', lambda e: self.toggle())
expanded_header_2.grid(column=1, row=0, sticky='ne')
# Show/expand the content frame if collapsible is opened
self.content_frame.grid(row=1, column=0, sticky='new', columnspan=2)
def toggle(self):
# Change value of self._variable and call self._activate
self._variable.set(not self._variable.get())
self._activate()
# Test the collapsible
if __name__ == "__main__":
# Create app and give it a name
app = tk.Tk()
app.title("Collapsible Test")
# Open app in full screen size
screen_width = app.winfo_screenwidth()
screen_height = app.winfo_screenheight()
app.geometry("%dx%d" % (screen_width, screen_height))
# Configure the 3-column layout
app.columnconfigure(0, weight=1)
app.columnconfigure(1, weight=1)
app.columnconfigure(2, weight=1)
# Define the 3 columns (= 3 frames with grid layout)
column_frame = None
column_frame_1 = tk.Frame(app)
column_frame_1.grid(row=0, column=0, sticky='new')
column_frame_2 = tk.Frame(app)
column_frame_2.grid(row=0, column=1, sticky='new')
column_frame_3 = tk.Frame(app)
column_frame_3.grid(row=0, column=2, sticky='new')
j = 0
for i in range(11):
# Check to which column the content should be added
if i >= 0 and i <= 2:
column_frame = column_frame_1
j = 0 if i == 0 else j
elif i >= 3 and i <= 5:
column_frame = column_frame_2
j = 0 if i == 3 else j
elif i >= 6:
column_frame = column_frame_3
j = 0 if i == 6 else j
# Create collapsible
collapsiblePane = Collapsible(parent=column_frame)
collapsiblePane.pack(fill='both', expand=1)
# This is the content of the collapsible
mytext = 'My '+str(i)+'. Label that contains a text.' if i < 9 else 'My '+str(i)+'. Label that contains an even longer text. The other columns are "jumping"!'
label = tk.Label(collapsiblePane.content_frame, text=mytext, justify='left', anchor='nw')
label.grid(row=0, column=0, sticky='nw')
# Raise the counters
i=i+1
j=j+1
# Start the app
app.mainloop()
(This first edit was made before changing the title, please read till the end!)
I am having problems while adjusting the screen of Tkinter on Windows 10.
I was doing things like that:
width_screen = root.winfo_screenwidth()
height_screen = root.winfo_screenheight()
root.geometry(f'{width_screen}x{height_screen}')
But the problem is that this configuration hide my taskbar... I search a way to set the screen like as it is for my browser for example, with a maximized window and a taskbar.
Thank you very much for your help.
EDIT 1: It is not working with this code...
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# -------------------------------- Importation ------------------------------- #
import os
import subprocess
import tkinter as tk
# ------------------------------ Initialisation ------------------------------ #
root = tk.Tk() #initialise l'application
root.title("Bruits ambiants pour l'écoute du patient")
width_screen = root.winfo_screenwidth()
height_screen = root.winfo_screenheight()
root.state('zoomed')
wav_files = ["a.wav","b.wav","c.wav","d.wav","e.wav","f.wav","g.wav","h.wav","i.wav","j.wav","k.wav","l.wav","m.wav","n.wav","o.wav","p.wav","q.wav","r.wav","s.wav","t.wav","u.wav","v.wav","w.wav","x.wav","y.wav","z.wav","aa.wav","bb.wav","cc.wav","dd.wav","ee.wav","ff.wav","gg.wav","hh.wav","ii.wav","jj.wav"]
# ---------------------------------------------------------------------------- #
# Vertical scrolled frame #
# ---------------------------------------------------------------------------- #
class VerticalScrolledFrame(tk.Frame):
def __init__(self, parent, *args, **kw):
tk.Frame.__init__(self, parent, *args, **kw)
# Create a frame for the canvas with non-zero row&column weights
self.frame_canvas = tk.Frame(self,bg="gray50")
self.frame_canvas.grid(row=2, column=0, sticky='nw')
self.frame_canvas.grid_rowconfigure(0, weight=1)
self.frame_canvas.grid_columnconfigure(0, weight=1)
self.parent=parent
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = tk.Scrollbar(self.frame_canvas, orient=tk.VERTICAL)
vscrollbar.grid(row=0, column=1, sticky='ns')
self.canvas = tk.Canvas(self.frame_canvas, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set, width=self.parent.winfo_screenwidth(),
height=self.parent.winfo_screenheight()-100)
self.canvas.grid(row=0, column=0, sticky="news")
vscrollbar.config(command=self.canvas.yview)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = tk.Frame(self.canvas,bg="gray50")
interior_id = self.canvas.create_window(0, 0, window=interior,
anchor=tk.NW)
self.interior.update_idletasks()
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
def _configure_canvas(event):
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the inner frame's width to fill the canvas
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(self, event):
if len(wav_files) > 25:
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
# ---------------------------------------------------------------------------- #
# Sound Buttons #
# ---------------------------------------------------------------------------- #
class Make_sound:
def __init__(self, name, parent, i):
self.varbutton = tk.StringVar()
self.name = name
self.parent = parent
self.num = i
self.soundbuttoncreator()
def soundbuttoncreator(self):
self.rows = self.num//4
self.columns = self.num%4
self.frame = tk.Frame(self.parent,bg="gray50", bd=3, relief="flat") # create a frame to hold the widgets
# use self.frame as parent instead of self.parent
self.button = tk.Checkbutton(self.frame, text=self.name.capitalize(), indicatoron=False, selectcolor="DeepSkyBlue3", background="slate gray", activebackground="LightSteelBlue3",variable=self.varbutton, command=self.launchsound, height=6, width=20)
self.button.pack()
self.button.bind("<Enter>", self.on_enter)
self.button.bind("<Leave>", self.on_leave)
self.frame.grid(row=self.rows, column=self.columns)
def on_enter(self, e):
self.button['background'] = 'LightSteelBlue3'
def on_leave(self, e):
self.button['background'] = 'slate gray'
def launchsound(self):
pass
def sounds_buttons(parent):
for i in range(len(wav_files)):
new_name = wav_files[i][:-4]
globals()["wav_files"][i] = Make_sound(new_name,parent,i)
def end_all():
for i in range(len(wav_files)):
globals()["wav_files"][i].varbutton.set("0")
try:
globals()["wav_files"][i].chan.stop()
except AttributeError:
pass
# ---------------------------------------------------------------------------- #
# Creation #
# ---------------------------------------------------------------------------- #
# ---------------------------------- Button ---------------------------------- #
frame_test = tk.Frame(root)
frame_test.grid(row=10,column=0, columnspan=5, sticky="s",padx=5,pady=10)
Button_open = tk.Button(frame_test, text="Open", background="slate gray", activebackground="LightSteelBlue3")
Button_open.pack(fill="x")
Button_end = tk.Button(frame_test, text="End", background="slate gray", activebackground="LightSteelBlue3")
Button_end.pack(fill="x")
# ---------------------------------------------------------------------------- #
# LEFT BUTTONS #
# ---------------------------------------------------------------------------- #
frame_buttons = tk.Frame(root,bd=5,bg="gray50")
frame_buttons.grid(row=1,column=0,rowspan=8,padx=5,pady=10,sticky="nw")
scframe = VerticalScrolledFrame(frame_buttons)
scframe.grid(row=1,column=0,rowspan=20,columnspan=3)
sounds_buttons(scframe.interior)
# ----------------------------------- test ----------------------------------- #
panel = tk.Button(root, text="test", background="slate gray", activebackground="LightSteelBlue3")
panel.grid(row=0,column=0,sticky="nw")
# ---------------------------------------------------------------------------- #
# ROOT #
# ---------------------------------------------------------------------------- #
root.mainloop()
(Some EDITS were suppressed for your readability)
LAST EDIT:
I found out that the problem comes from a part of my class:
self.canvas = tk.Canvas(self.frame_canvas, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set, width=self.parent.winfo_screenwidth(),
height=self.parent.winfo_screenheight()-100)
In fact, the height=self.parent.winfo_screenheight()-100 part is not working properly. If I put height=self.parent.winfo_screenheight()-1000, here is my output:
It is promising, because I now see the frame. Now, I understand that I just want the canvas to be responsive and not to set height and width, though I can work with it many computers!
Could you please explain me a way to achieve that? Maybe for example, always having 4 columns of buttons but with their dimensions that can change, and setting the button list to always be taking the rest of the screen (we could say that it may take the half of the screen width, and that the buttons over and below must grow as the button list grows?).
Try this code below:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# -------------------------------- Importation ------------------------------- #
import os
import subprocess
import tkinter as tk
# ------------------------------ Initialisation ------------------------------ #
root = tk.Tk() # initialise l'application
root.title("Bruits ambiants pour l'écoute du patient")
width_screen = root.winfo_screenwidth()
height_screen = root.winfo_screenheight()
root.state('zoomed')
wav_files = ["a.wav", "b.wav", "c.wav", "d.wav", "e.wav", "f.wav", "g.wav", "h.wav", "i.wav", "j.wav", "k.wav", "l.wav",
"m.wav", "n.wav", "o.wav", "p.wav", "q.wav", "r.wav", "s.wav", "t.wav", "u.wav", "v.wav", "w.wav", "x.wav",
"y.wav", "z.wav", "aa.wav", "bb.wav", "cc.wav", "dd.wav", "ee.wav", "ff.wav", "gg.wav", "hh.wav", "ii.wav",
"jj.wav"]
# ---------------------------------------------------------------------------- #
# Vertical scrolled frame #
# ---------------------------------------------------------------------------- #
class VerticalScrolledFrame(tk.Frame):
def __init__(self, parent, *args, **kw):
tk.Frame.__init__(self, parent, *args, **kw)
# Create a frame for the canvas with non-zero row&column weights
self.parent = parent
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
vscrollbar.pack(fill="y", side="right",expand=True)
self.canvas = tk.Canvas(self, bd=0, highlightthickness=1,
yscrollcommand=vscrollbar.set)
self.canvas.pack(fill="both", expand=True)
vscrollbar.config(command=self.canvas.yview)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = tk.Frame(self.canvas, bg="gray50")
interior_id = self.canvas.create_window(0, 0, window=interior,
anchor=tk.NW)
self.interior.update_idletasks()
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
self.canvas.config(scrollregion=self.canvas.bbox("all"))
def _configure_canvas(event):
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the inner frame's width to fill the canvas
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(self, event):
if len(wav_files) > 25:
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
# ---------------------------------------------------------------------------- #
# Sound Buttons #
# ---------------------------------------------------------------------------- #
class Make_sound:
def __init__(self, name, parent, i):
self.varbutton = tk.StringVar()
self.name = name
self.parent = parent
self.num = i
self.soundbuttoncreator()
def soundbuttoncreator(self):
self.rows = self.num // 4
self.columns = self.num % 4
self.frame = tk.Frame(self.parent, bg="gray50", bd=3, relief="flat") # create a frame to hold the widgets
# use self.frame as parent instead of self.parent
self.button = tk.Checkbutton(self.frame, text=self.name.capitalize(), indicatoron=False,
selectcolor="DeepSkyBlue3", background="slate gray",
activebackground="LightSteelBlue3", variable=self.varbutton,
command=self.launchsound, height=6, width=20)
self.button.pack()
self.button.bind("<Enter>", self.on_enter)
self.button.bind("<Leave>", self.on_leave)
self.frame.grid(row=self.rows, column=self.columns)
def on_enter(self, e):
self.button['background'] = 'LightSteelBlue3'
def on_leave(self, e):
self.button['background'] = 'slate gray'
def launchsound(self):
pass
def sounds_buttons(parent):
for i in range(len(wav_files)):
new_name = wav_files[i][:-4]
globals()["wav_files"][i] = Make_sound(new_name, parent, i)
def end_all():
for i in range(len(wav_files)):
globals()["wav_files"][i].varbutton.set("0")
try:
globals()["wav_files"][i].chan.stop()
except AttributeError:
pass
# ---------------------------------------------------------------------------- #
# Creation #
# ---------------------------------------------------------------------------- #
# ---------------------------------- Button ---------------------------------- #
frame_test = tk.Frame(root)
frame_test.grid(row=10, column=0, columnspan=5, sticky="ns")
Button_open = tk.Button(frame_test, text="Open", background="slate gray", activebackground="LightSteelBlue3")
Button_open.grid(row=0, column=0, sticky="ns")
Button_end = tk.Button(frame_test, text="End", background="slate gray", activebackground="LightSteelBlue3")
Button_end.grid(row=1, column=0, sticky="ns")
# ---------------------------------------------------------------------------- #
# LEFT BUTTONS #
# ---------------------------------------------------------------------------- #
frame_buttons = tk.Frame(root, bd=5, bg="gray50")
frame_buttons.grid(row=1, column=0, rowspan=8, padx=5, pady=10, sticky="nwes")
scframe = VerticalScrolledFrame(frame_buttons)
scframe.pack(fill="both", expand=True)
sounds_buttons(scframe.interior)
# ----------------------------------- test ----------------------------------- #
panel = tk.Button(root, text="test", background="slate gray", activebackground="LightSteelBlue3")
panel.grid(row=0, column=0, sticky="nw")
# ---------------------------------------------------------------------------- #
# ROOT #
# ---------------------------------------------------------------------------- #
for i in range(1, 11):
root.grid_rowconfigure(i, weight=1)
for i in range(frame_test.grid_size()[1]+1):
frame_test.grid_rowconfigure(i, weight=1)
root.mainloop()
Too much code,A little hard to understand your layout.
I changed a lot from your code,your frame_buttons doesn't use sticky="nwes".So it couldn't fill in the frame.
And in the canvas, you also need to use pack manager(if you didn't use sticky="nwes" and set the rowconfigure, I still recommend you use pack).
for responsive, you need to set gird_rowconfigure
check the code for more details.
The output:
If there only one button on the canvas, the scrollbar is disabled:
Can somebody with more tkinter experience than me please have a look at this code and tell me how I could refactor it.
The code builts on: tkinter gui layout using frames and grid
I have particular doubt about:
-grid and place i.e. is it possible to center a widget in a frame using only grid?
-global and the way I keep track of the active frame - is there a better way?
from tkinter import *
def set_ret_btn_vis(new_visibility):
if(new_visibility):
ret_btn.grid(row=1, column=2, padx=50)
else:
ret_btn.grid_forget()
def show_main_menu():
#populate the main frame with two action buttons
print("I am the Main Menu")
global active_frame
if(active_frame!=main_menu):
active_frame.grid_forget()
main_menu.grid(row=1,sticky="ew")
task1_btn.place(relx =0.2, rely=0.45)
task2_btn.place(relx =0.5, rely=0.45)
def show_task1_frame():
print("I am Task1")
global active_frame
active_frame = task1
main_menu.grid_forget()
task1.grid(row=1,sticky="ew")
def show_task2_frame():
print("I am Task2")
global active_frame
active_frame = task2
main_menu.grid_forget()
task2.grid(row=1,sticky="ew")
root = Tk()
root.title("IM-Tools")
root.geometry("{}x{}".format(400,350))
# creates all the frames
top_frame = Frame(root, bg="red", width=400,height=50)
main_menu = Frame(root,bg="#FFAAFF",width=400,height=250 )
task1 = Frame(root,bg="#00CCDD", width=400,height=250)
task2 = Frame(root,bg="#AA00C0", width=400,height=250)
bottom_frame = Frame(root, bg="yellow", width=400,height=50)
# stops frame from shrinking when widget is placed inside
top_frame.grid_propagate(0)
bottom_frame.grid_propagate(0)
# layout main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)
top_frame.grid(row=0)
main_menu.grid(row=1)
bottom_frame.grid(row=2)
# create all widgets used by the different frames
# top frame
ret_btn = Button(top_frame,text="Main Menu", width=10, command= lambda: [set_ret_btn_vis(False), show_main_menu()] )
# main frame
task1_btn = Button(main_menu, text="Action1", command= lambda: [ set_ret_btn_vis(True),show_task1_frame()] )
task2_btn = Button(main_menu, text="Action2", command= lambda: [ set_ret_btn_vis(True),show_task2_frame()] )
name_lbl = Label(bottom_frame, text="my name", bg='yellow')
name_lbl.place(relx=0.4, rely=0.45)
# keeps track of currently active frame
active_frame = main_menu
# first
show_main_menu()
root.mainloop()
Thank you very much #Atlas435 I changed the code accordingly - I hope this will help someone after me.
from tkinter import *
class IM_UI(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
# creates all the frames
self.top_frame = Frame(self.parent, bg="red", width=400,height=50)
self.main_menu = Frame(self.parent,bg="#FFAAFF",width=400,height=250 )
self.task1 = Frame(self.parent,bg="#00CCDD", width=400,height=250)
self.task2 = Frame(self.parent,bg="#AA00C0", width=400,height=250)
self.bottom_frame = Frame(self.parent, bg="yellow", width=400,height=50)
# stops frame from shrinking when widget is placed inside
self.top_frame.grid_propagate(0)
self.main_menu.grid_propagate(0)
self.bottom_frame.grid_propagate(0)
# layout main containers
self.top_frame.grid(row=0)
self.main_menu.grid(row=1)
self.bottom_frame.grid(row=2)
# make row one auto-extend / make column zero auto-extend
self.parent.grid_rowconfigure(1, weight=1)
self.parent.grid_columnconfigure(0, weight=1)
# bottom_frame has onw row / by saying ( 0, weight=1 ) it allows the row to grow with the size of the frame
self.bottom_frame.grid_rowconfigure(0,weight=1)
self.bottom_frame.grid_columnconfigure(0,weight=1)
# create all widgets used by the different frames
# top frame
self.ret_btn = Button(self.top_frame,text="Main Menu", width=10, command= lambda: [self.set_ret_btn_vis(False), self.show_main_menu()] )
# main frame
self.task1_btn = Button(self.main_menu, text="Action1", command= lambda: [ self.set_ret_btn_vis(True),self.show_task1_frame()] )
self.task2_btn = Button(self.main_menu, text="Action2", command= lambda: [ self.set_ret_btn_vis(True),self.show_task2_frame()] )
# configure the grid / rows of the main frame
# have to columns equally weighted and one row
self.main_menu.grid_columnconfigure(0, weight=1)
self.main_menu.grid_columnconfigure(1, weight=1)
self.main_menu.grid_rowconfigure(0,weight=1)
# with the sticky options the buttons expand
self.task1_btn.grid(column=0, row=0, columnspan=1)#, sticky="ew")
self.task2_btn.grid(column=1, row=0, columnspan=1)#, sticky="ew")
# bottom frame
self.name_lbl = Label(self.bottom_frame, text="my name", bg='yellow')
self.name_lbl.grid(column=0, columnspan=1, sticky="nsew")
# keeps track of currently active frame
self.active_frame = self.main_menu
def set_ret_btn_vis(self,new_visibility):
if(new_visibility):
self.ret_btn.grid(row=1, column=2, padx=50)
else:
self.ret_btn.grid_forget()
def show_main_menu(self):
#populate the main frame with two action buttons
print("I am the Main Menu")
self.active_frame.grid_forget()
self.main_menu.grid(row=1,sticky="nsew")
def show_task1_frame(self):
print("I am Task1")
self.main_menu.grid_forget()
self.task1.grid(row=1,sticky="ew")
#set the new active frame
self.active_frame = self.task1
def show_task2_frame(self):
print("I am Task2")
self.main_menu.grid_forget()
self.task2.grid(row=1,sticky="ew")
#set the new active frame
self.active_frame = self.task2
root = Tk()
root.title("IM-Tools")
root.geometry("{}x{}".format(400,350))
root.resizable(False,False)
my_UI = IM_UI(root)
root.mainloop()
I try to create a scrolling grid of frames. I will have a variable number of frames, so I want to specify the number of columns, but not the number of rows, though we can scroll down if it is too long.
I have written some code, but now I don't know how to specify that when creating a frame (a button + 2 scales) with sounds_buttons(), it belongs to a certain row and a certain column. Also, I won't always have a multiple of 5 for the number of frames, so the last row can be constituted of only 1,2,3 or 4 frames.
The goal would be to have something like that, but with an undetermined number of rows: tkinter Canvas Scrollbar with Grid?.
Thank you very much !
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# -------------------------------- Importation ------------------------------- #
import tkinter as tk
from tkinter import messagebox
# ------------------------------ Initialisation ------------------------------ #
root = tk.Tk()
width_screen, height_screen = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (width_screen, height_screen))
# ----------------------- Creation of a list of sounds ----------------------- #
wav_files = ["a.wav","b.wav","c.wav","d.wav","e.wav","f.wav","g.wav","h.wav","i.wav","j.wav","k.wav","l.wav","m.wav","n.wav","o.wav","p.wav","q.wav","r.wav","s.wav","t.wav","u.wav","v.wav","w.wav","x.wav","y.wav","z.wav","aa.wav","bb.wav","cc.wav","dd.wav","ee.wav","ff.wav"]
# -------------------------- Vertical scrolled frame ------------------------- #
class VerticalScrolledFrame(tk.Frame):
def __init__(self, parent, *args, **kw):
tk.Frame.__init__(self, parent, *args, **kw)
# Create a frame for the canvas with non-zero row&column weights
self.frame_canvas = tk.Frame(self)
self.frame_canvas.grid(row=2, column=0, pady=(8, 0), sticky='nw')
self.frame_canvas.grid_rowconfigure(0, weight=1)
self.frame_canvas.grid_columnconfigure(0, weight=1)
# Set grid_propagate to False to allow buttons resizing later
self.frame_canvas.grid_propagate(False)
# create a canvas object and a vertical scrollbar for scrolling it
vscrollbar = tk.Scrollbar(self.frame_canvas, orient=tk.VERTICAL)
vscrollbar.grid(row=0, column=1, sticky='ns')
self.canvas = tk.Canvas(self.frame_canvas, bd=0, highlightthickness=0,
yscrollcommand=vscrollbar.set, width=root.winfo_screenwidth(), height=root.winfo_screenheight())
self.canvas.grid(row=0, column=0, sticky="news")
vscrollbar.config(command=self.canvas.yview)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
# reset the view
#canvas.xview_moveto(0)
#canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = tk.Frame(self.canvas)
interior_id = self.canvas.create_window(0, 0, window=interior,
anchor=tk.NW)
self.interior.update_idletasks()
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != self.canvas.winfo_width():
# update the inner frame's width to fill the canvas
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
# ------------------------------- Sound buttons ------------------------------ #
class Make_sound:
def __init__(self, name, parent):
self.varbutton = tk.StringVar()
self.name = name
self.parent = parent
self.soundbuttoncreator()
def launchsound(self):
print(self.varbutton.get())
if self.varbutton.get() == 1:
self.list=[]
else:
self.list.append("A")
def soundbuttoncreator(self):
self.frame = tk.Frame(self.parent) # create a frame to hold the widgets
# use self.frame as parent instead of self.parent
self.volumescale = tk.Scale(self.frame, orient='vertical', from_=100, to=0, resolution=0.5, label='Volume',command=self.setvolume, cursor="fleur")
self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew")
self.volumescale.set(100)
self.faderscale = tk.Scale(self.frame, orient='horizontal', from_=-1, to=1, resolution=0.01, label='Balance G/D', command=self.setbalance, cursor="fleur")
self.faderscale.grid(row=1,column=0, sticky="nsew")
self.button = tk.Checkbutton(self.frame, text=self.name, indicatoron=False, selectcolor="green", background="red", variable=self.varbutton, command=self.launchsound, cursor="plus")
self.button.grid(row=0, column=0, sticky="nsew")
self.frame.pack() # use pack() on the frame so new instance of `Make_sound` will not overlap the old instances
def setvolume(self,event):
pass
def setbalance(self,event):
pass
def sounds_buttons(parent):
for i in range(len(wav_files)):
new_name = wav_files[i][:-4]
globals()["wav_files"][i] = Make_sound(new_name,parent)
# ---------------------------------------------------------------------------- #
# Creation #
# ---------------------------------------------------------------------------- #
# ----------------------------- Buttons of sound ----------------------------- #
scframe = VerticalScrolledFrame(root)
scframe.pack(side=tk.LEFT)
sounds_buttons(scframe.interior)
root.mainloop()
EDIT 1:
I have modified the Make_sound class and sounds_buttons function. There is an error.
class Make_sound:
def __init__(self, name, parent, i):
self.varbutton = tk.StringVar()
self.name = name
self.parent = parent
self.num = i
self.soundbuttoncreator()
def launchsound(self):
print(self.varbutton.get())
if self.varbutton.get() == 1:
self.list=[]
else:
self.list.append("A")
def soundbuttoncreator(self):
self.rows = self.num//5
self.columns = self.num%5
self.frame = tk.Frame(self.parent) # create a frame to hold the widgets
# use self.frame as parent instead of self.parent
self.volumescale = tk.Scale(self.frame, orient='vertical', from_=100, to=0, resolution=0.5, label='Volume',command=self.setvolume, cursor="fleur")
self.volumescale.grid(row=0,column=1, rowspan=2, sticky="nsew")
self.volumescale.set(100)
self.faderscale = tk.Scale(self.frame, orient='horizontal', from_=-1, to=1, resolution=0.01, label='Balance G/D', command=self.setbalance, cursor="fleur")
self.faderscale.grid(row=1,column=0, sticky="nsew")
self.button = tk.Checkbutton(self.frame, text=self.name, indicatoron=False, selectcolor="green", background="red", variable=self.varbutton, command=self.launchsound, cursor="plus")
self.button.grid(row=0, column=0, sticky="nsew")
self.frame.grid(row=self.rows, column=self.columns)
def setvolume(self,event):
pass
def setbalance(self,event):
pass
def sounds_buttons(parent):
for i in range(len(wav_files)):
new_name = wav_files[i][:-4]
globals()["wav_files"][i] = Make_sound(new_name,parent,i)
I'm trying a simple experiment, I have 3 frames, frame 1 has two labels - "for page 2" and "for page 3", it also has 2 radio buttons corresponding to the labels. based on which radio button is selected, when the user hits the next page button, I want the button to bring the user to the selected page
this is the code -
import Tkinter as tk
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the main container that holds all the frames
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 = {}
# adding frames to the dictionary
for F in (Page1,Page2,Page3):
frame = F(container,self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "w")
self.show_frame(Page1)
def show_frame(self,page_name):
#SHOWS A FRAME WITH THE GIVEN NAME
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
#STACKING THE FRAMES
#frame = self.frames[cont]
#frame.tkraise()
class Page1(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl1 = tk.Label(self,text = "for page 2",font =("Helvetica",12,"bold"))
lbl1.grid(row=1,sticky="W")
lbl2 = tk.Label(self,text = "for page 3",font =("Helvetica",12,"bold"))
lbl2.grid(row=1,column=1,sticky="W")
btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
btn1.grid(row=3,column = 0,columnspan=1)
#btn1['command'] = lambda: controller.show_frame(Page2)
self.var1 = tk.BooleanVar()
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
rButton2.grid(row=2,column=1,sticky = "W")
if self.var1.get() == 1:
btn1['command'] = lambda: controller.show_frame(Page3)
if self.var1.get() == 0:
btn1['command'] = lambda: controller.show_frame(Page2)
class Page2(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 2",font=("Helvetica",12,"bold"))
lbl.pack()
class Page3(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 3",font=("Helvetica",12,"bold"))
lbl.pack()
app = MainApp()
app.mainloop()
I assumed that by using a few basic conditions (located in my PageOne class) -
self.var1 = tk.BooleanVar()
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False)
rButton2.grid(row=2,column=1,sticky = "W")
if self.var1.get() == 1:
btn1['command'] = lambda: controller.show_frame(Page3)
if self.var1.get() == 0:
btn1['command'] = lambda: controller.show_frame(Page2)
I would be able to achieve this, but it doesn't seem to work. The conditions in my if statements are integers but to my knowledge 1 represents True and 0; False anyway? what am i doing wrong?
I think this is what you want. I didn't handle making sure the radiobutton isn't selected by default. I left that as an exercise to you. Although, if you're wanting to just switch pages like this I'd just use buttons (tk/ttk.Button), then you don't have to worry about handling the radiobutton. Although, that's just my preference either will work fine of course. You can just bind each button to switch the page. I commented the buttons out in your modified code below.
If you're wanting to create buttons / radiobuttons to have a forward / back option for each page. You can just iterate over the controllers frames to see which is the current, and create two buttons similar to the ones below to move to the other frames.
import tkinter as tk
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# the main container that holds all the frames
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 = {}
# adding frames to the dictionary
for F in (Page1,Page2,Page3):
frame = F(container,self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "w")
self.show_frame(Page1)
def show_frame(self,page_name):
#SHOWS A FRAME WITH THE GIVEN NAME
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
#STACKING THE FRAMES
#frame = self.frames[cont]
#frame.tkraise()
class Page1(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller = controller
lbl1 = tk.Label(self,text = "for page 2",font =("Helvetica",12,"bold"))
lbl1.grid(row=1,sticky="W")
lbl2 = tk.Label(self,text = "for page 3",font =("Helvetica",12,"bold"))
lbl2.grid(row=1,column=1,sticky="W")
btn1 = tk.Button(self, text="next page", font=('MS', 24, 'bold'))
btn1.grid(row=3,column = 0,columnspan=1)
#btn1['command'] = lambda: controller.show_frame(Page2)
self.var1 = tk.BooleanVar()
#rButton1 = tk.Button(self, text='Show Page 2', command=lambda: self.controller.show_frame(Page2))
#rButton1.grid(row=2, sticky="W")
#rButton2 = tk.Button(self, text='Show Page 3', command=lambda: self.controller.show_frame(Page3))
#rButton2.grid(row=2, column=1, sticky="W")
rButton1 = tk.Radiobutton(self,variable = self.var1,value=True,
command=self.switch_pages)
rButton1.grid(row=2,sticky = "W")
rButton2 = tk.Radiobutton(self,variable = self.var1,value=False,
command=self.switch_pages)
rButton2.grid(row=2,column=1,sticky = "W")
def switch_pages(self):
if not self.var1.get():
self.controller.show_frame(Page3)
else:
self.controller.show_frame(Page2)
class Page2(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 2",font=("Helvetica",12,"bold"))
lbl.pack()
class Page3(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
lbl = tk.Label(self,text="This is page 3",font=("Helvetica",12,"bold"))
lbl.pack()
app = MainApp()
app.mainloop()