tkinter collapsible in 3-column layout makes columns jump - python

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

Related

Why is my tkinter treeview not changing colors?

I am trying to color my tkinter treeview using tags but it doesn't work even tho I have followed a few tutorials and I think I am doing everything the right way.
self.sidebar_button_event()
self.ultimo = "Inventario"
self.cajas_frame = customtkinter.CTkTabview(self, height=250)
self.cajas_frame.add("Cajas")
self.cajas_frame.tab("Cajas").grid_columnconfigure(0, weight=1)
self.cajas_frame.tab("Cajas").grid_rowconfigure(0, weight=1)
self.cajas_frame.grid(row=0, column=1, padx=(20, 20), pady=(20, 20), sticky="new", columnspan=3)
self.setTablaCajas()
n = 0
for f in self.inventario.datosCajas():
if n % 2 == 0:
self.cajas.insert(parent='', index='end', iid=n, values=f, tags=('par',))
else:
self.cajas.insert(parent='', index='end', iid=n, values=f, tags=('impar',))
self.cajas.bind("<<TreeviewSelect>>", self.clickCajas)
n += 1
n = 0
self.cajas.tag_configure('par', background='orange', )
self.cajas.tag_configure('impar', background='purple')
Could it be because of me using customtkinter and tkinter?
PS: I already tried using Style.configure and it does change it's appearance, but it doesn't seem to work this way and I want odd and even rows to have a different color.
I used a working example to explain the color tag function for the rows:
# Source for base treeview https://www.pythontutorial.net/tkinter/tkinter-treeview/
# extendet with style and color line 11 to 13, 24, 33 to 38 and 60,61
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
root = tk.Tk()
root.title('Treeview demo')
root.geometry('620x200')
# style the widget
s = ttk.Style()
s.theme_use('clam')
# define columns
columns = ('first_name', 'last_name', 'email')
tree = ttk.Treeview(root, columns=columns, show='headings')
# define headings
tree.heading('first_name', text='First Name')
tree.heading('last_name', text='Last Name')
tree.heading('email', text='Email')
s.configure('Treeview.Heading', background="green3")
# generate sample data
contacts = []
for n in range(1, 100):
contacts.append((f'first {n}', f'last {n}', f'email{n}#example.com'))
# add data to the treeview AND tag the row color
i = 1
for contact in contacts:
i += 1
if i%2:
tree.insert('', tk.END, values=contact, tags = ('oddrow',))
else:
tree.insert('', tk.END, values=contact, tags = ('evenrow',))
def item_selected(event):
for selected_item in tree.selection():
item = tree.item(selected_item)
record = item['values']
# show a message
showinfo(title='Information', message=','.join(record))
tree.bind('<<TreeviewSelect>>', item_selected)
tree.grid(row=0, column=0, sticky='nsew')
# add a scrollbar
scrollbar = ttk.Scrollbar(root, orient=tk.VERTICAL, command=tree.yview)
tree.configure(yscroll=scrollbar.set)
scrollbar.grid(row=0, column=1, sticky='ns')
# style row colors
tree.tag_configure('oddrow', background='lightgrey')
tree.tag_configure('evenrow', background='white')
# run the app
root.mainloop()
Output:

Adding a header line to a scrollable canvas with weights

I'm trying get a list of .xlsm files from a folder, and generate a scrollable canvas from which the tabs needed for import can be selected manually using the check buttons (all having the same tab format e.g. tab1, tab2, tab3, tab4).
The major issue I'm having is getting weights to work correctly for the headers in relation to their canvas columns, as longer file names distorts the weight.
I've tried playing with the weights and can't seem to figure out a workaround. I also attempted using treeview as an alternative but this seems to introduce far bigger issues with using checkbuttons. Would it possible to freeze the top row if the headers were placed inside the canvas itself, or could I implement something like a bind so that the header frames individual columns align with the width of the columns of the canvas frame?
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
class MainFrame:
def __init__(self, master):
master.geometry('1000x200')
self.master_tab = ttk.Notebook(master)
self.master_tab.grid(row=0, column=0, sticky='nsew')
# Sub-Classes
self.file_select = FileSelect(self.master_tab, main=self)
class FileSelect:
def __init__(self, master, main):
self.main = main
# ================== Primary Frame ==================
self.primary_frame = tk.Frame(master)
self.primary_frame.grid(row=0, column=0, sticky='NSEW')
master.add(self.primary_frame, text='Import Selection')
self.primary_frame.columnconfigure(0, weight=1)
self.primary_frame.rowconfigure(1, weight=1)
# ================== File Selection Frame ==================
self.selection_frame = tk.Frame(self.primary_frame)
self.selection_frame.grid(row=0, column=0, sticky='EW')
# Button - Select Directory
self.fp_button = tk.Button(self.selection_frame, text='Open:', command=self.directory_path)
self.fp_button.grid(row=0, column=0, sticky='W')
# Label - Display Directory
self.fp_text = tk.StringVar(value='Select Import Directory')
self.fp_label = tk.Label(self.selection_frame, textvariable=self.fp_text, anchor='w')
self.fp_label.grid(row=0, column=1, sticky='W')
# ================== Canvas Frame ==================
self.canvas_frame = tk.Frame(self.primary_frame)
self.canvas_frame.grid(row=1, column=0, sticky='NSEW')
self.canvas_frame.rowconfigure(1, weight=1)
# Canvas Header Labels
for header_name, x in zip(['File Name', 'Tab 1', 'Tab 2', 'Tab 3', 'Tab 4'], range(5)):
tk.Label(self.canvas_frame, text=header_name, anchor='w').grid(row=0, column=x, sticky='EW')
self.canvas_frame.columnconfigure(x, weight=1)
# Scroll Canvas
self.canvas = tk.Canvas(self.canvas_frame, bg='#BDCDFF')
self.canvas.grid(row=1, column=0, columnspan=5, sticky='NSEW')
self.canvas.bind('<Configure>', self.frame_width)
# Scrollbar
self.scroll_y = tk.Scrollbar(self.canvas_frame, orient="vertical", command=self.canvas.yview)
self.scroll_y.grid(row=1, column=5, sticky='NS')
# Canvas Sub-Frame
self.canvas_sub_frame = tk.Frame(self.canvas)
for x in range(5):
self.canvas_sub_frame.columnconfigure(x, weight=1)
self.canvas_frame_window = self.canvas.create_window(0, 0, anchor='nw', window=self.canvas_sub_frame)
self.canvas_sub_frame.bind('<Configure>', self.config_frame)
def config_frame(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox('all'), yscrollcommand=self.scroll_y.set)
def frame_width(self, event):
canvas_width = event.width
event.widget.itemconfigure(self.canvas_frame_window, width=canvas_width)
def directory_path(self):
try:
# Select file path
directory = filedialog.askdirectory(initialdir='/', title='Select a directory')
self.fp_text.set(str(directory))
os.chdir(directory)
# Updates GUI with .xlsm file list & checkboxes
if len(os.listdir(directory)) != 0:
y = -1
for tb in os.listdir(directory):
if not tb.endswith('.xlsm'):
print(str(tb) + ' does not have ;.xlsm file extension')
else:
y += 1
file_name = tk.Label(self.canvas_sub_frame, text=tb, anchor='w', bg='#96ADF3')
file_name.grid(row=y, column=0, sticky='EW')
for x in range(4):
tb_period = tk.Checkbutton(self.canvas_sub_frame, anchor='w', bg='#C2D0F9')
tb_period.grid(row=y, column=x+1, sticky='EW')
else:
print('No files in directory')
# Filepath error handling exception
except os.error:
print('OS ERROR')
if __name__ == '__main__':
root = tk.Tk()
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
MainFrame(root)
root.mainloop()
The simplest solution is to use two canvases, and then set up a binding so that whenever the size of the inner frame changes, you update the headers to match the columns.
It might look something like this:
def config_frame(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox('all'), yscrollcommand=self.scroll_y.set)
self.canvas.after_idle(self.reset_headers)
def reset_headers(self):
for column in range(self.canvas_sub_frame.grid_size()[0]):
bbox = self.canvas_sub_frame.grid_bbox(column, 0)
self.canvas_frame.columnconfigure(column, minsize = bbox[2])

How do I create a scrollbar when using a grid in tkinter?

I'm a little bit stuck on this problem regarding my program. I tried adding as many comments as possible to give a sense of what everything does in the code, but essentially. The program has a field and value entry box. When the "add field/value button" is clicked, more of the entry widgets are added. If this keeps occurring then obviously it'll go off screen. So I've limited the size of the application, but the problem then is I need a scrollbar. I've tried looking it up, but my frame uses grid, and everywhere they use pack which isn't compatible in this case. I get the scrollbar to appear, however it doesn't seem to work. I've seen some people use canvas, and more than one frame, etc. I'm missing something important but I don't know how do the exact same thing with a grid. Think you experts can lend me hand to get it working?
from tkinter import *
import tkinter as tk
class Insert(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.grid_columnconfigure(0, weight=1)
container.grid_rowconfigure(0, weight=1)
container.pack(side="top", fill="both", expand=True)
self.frameslist = {}
for frame in (Create,):
frame_occurrence = frame.__name__
active_frame = frame(parent=container, controller=self)
self.frameslist[frame_occurrence] = active_frame
active_frame.grid(row=0, column=0, sticky="snew")
self.show_frame("Create")
def show_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
active_frame.tkraise()
class Create(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
#For all widgets (nested list, 2 widgets per row)
self.inputlist = []
#For just the entries
self.newinputlist = []
#Create two labels, add them into the inputlist to be iterated
labels = [tk.Label(self, text="Field"), tk.Label(self, text="Values")]
self.inputlist.append(labels[:])
#Insert the labels from the list
for toplabels in range(1):
self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)
#Create the first two entry boxes, append them to the inputlist, and newinput list
first_entries = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.newinputlist.append(first_entries[:])
self.inputlist.append(first_entries[:])
#Insert the entries from the newinputlist
for x in range(0, len(self.newinputlist) + 1):
self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5)
#Create two buttons (Both share same row), append them to list
button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK")]
self.inputlist.append(button_input_1[:])
#Insert buttons at the bottom of the grid
for button in range(len(self.inputlist) - 2, len(self.inputlist)):
self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)
def add_insert(self):
#Create two new entries, append them to the list
add_input = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.inputlist.insert(-1, add_input)
self.newinputlist.append(add_input)
#Because there are new entry boxes, old grid should be forgotten
for widget in self.children.values():
widget.grid_forget()
#Use the index for the row, get all widgets and place them again
for index, widgets in enumerate(self.inputlist):
widget_one = widgets[0]
widget_two = widgets[1]
widget_one.grid(row=index, column=0, padx=10, pady=5)
widget_two.grid(row=index, column=1, padx=10)
#Create scrollbar when this button is pressed
scrollbar = tk.Scrollbar(self, orient="vertical")
scrollbar.grid(row=0, column=2, stick="ns", rowspan=len(self.inputlist) + 1)
if __name__ == "__main__":
app = Insert()
app.maxsize(0, 500)
app.mainloop()
You could create a Canvas and insert your Entry objects into a Frame.
Here is a simplified example that creates a 2D bank of Buttons using the canvas.create_window.
import tkinter as tk
root = tk.Tk()
# essential to enable full window resizing
root.rowconfigure(0, weight = 1)
root.columnconfigure(0, weight = 1)
# scrollregion is also essential when using scrollbars
canvas = tk.Canvas(
root, scrollregion = "0 0 2000 1000", width = 400, height = 400)
canvas.grid(row = 0, column = 0, sticky = tk.NSEW)
scroll = tk.Scrollbar(root, orient = tk.VERTICAL, command = canvas.yview)
scroll.grid(row = 0, column = 1, sticky = tk.NS)
canvas.config(yscrollcommand = scroll.set)
# I've used a labelframe instead of frame so button are neatly collected and named
frame = tk.LabelFrame(root, labelanchor = tk.N, text = "Buttonpad")
# Note I've placed buttons in frame
for fila in range(20):
for col in range(5):
btn = tk.Button(frame, text = f"{fila}-{col}")
btn.grid(row = fila, column = col, sticky = tk.NSEW)
# Frame is now inserted into canvas via create_window method
item = canvas.create_window(( 2, 2 ), anchor = tk.NW, window = frame )
root.mainloop()

Refreshing frame with Tkinter

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.

Tkinter python layout grid pack place

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

Categories

Resources