Adding a header line to a scrollable canvas with weights - python

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

Related

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

How can i put my taskbar to be always on top?

I want to place my menubar frame to the top of the window like the tkinter's Menu module.
class My_Menu:
def __init__(self, master, name="Default", expand="full", mode="bar"):
##### create the frame #####
self.menus = {}
self.master = master
self.master.columnconfigure(0, weight=1)
self.master.rowconfigure(0, weight=0)
self.master_frame = Frame(self.master)
self.master_frame.grid(row=0, column=0, sticky=NSEW)
self.master_frame.columnconfigure(0, weight=1)
self.master_frame.rowconfigure(0, weight=1)
self.main_frame = Frame(self.master_frame)
self.main_frame.grid(row=0, column=0, sticky=NSEW)
self.main_frame.rowconfigure(0, weight=0)
I am not sure if there is any way to do this, but a way around will be to create space to the row and use sticky to put the menu on top always.
from tkinter import *
class MenuFrame(Frame):
def __init__(self,parent,*args,**kwargs):
Frame.__init__(self,parent,*args,**kwargs)
self.b1 = Button(self,text='File',width=50)
self.b1.grid(row=0,column=0)
self.b2 = Button(self,text='Help',width=50)
self.b2.grid(row=0,column=1)
def ret_max(self):
self.update()
return self.b1.winfo_height()
root = Tk()
menu = MenuFrame(root)
menu.grid(row=0,column=0,sticky='n') # Can move this line in or out of class
height = menu.ret_max()
root.grid_rowconfigure(0,pad=height) # Make it have extra space of height of button
Button(root,text='Dummy Button').grid(row=0,column=0,sticky='s')
root.mainloop()

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

Tkinter how to size fonts and keep alignment

I'm writing an app that needs to be zoomable. Using the default system fonts, "TkDefaultFont and TkTextFont" I increase their sizes and I get the results I want sort of. The problem is after sizing the alignment is thrown off between the field label and field widget. I have tried applying update_idletasks() but it does nothing. Moving the mouse over the widget fixes the problem as see in the video. If you run the example below go the the size spinbox and change the size to view the issue.
My dev system is Linux, Desktop KDE, I don't know if this is a OS issue.
A link to a short video of the issue.
Video of Alignment Issue.
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
root = tk.Tk()
root.rowconfigure(0, weight=1)
root.columnconfigure(99, weight=1)
frame = ttk.Frame(root)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
cbo = ttk.Combobox(frame)
cbo.config(
values=('Test 1', 'Test 2', 'Test 3')
)
cbo.set(value='Test 1')
ent_var = tk.StringVar()
ent = ttk.Entry(frame, textvariable=ent_var)
ent_var.set('Test')
lb_size = ttk.Label(frame, text='size')
spn = ttk.Spinbox(frame, values=tuple(range(1, 101)))
font1 = tkfont.nametofont('TkDefaultFont')
font2 = tkfont.nametofont('TkTextFont')
lbl_field_name = tk.Label(frame, text='Field Name')
lbl_field_name.grid()
def size(e):
cfg = font1.actual()
cfg['size'] = e.widget.get()
font1.configure(**cfg)
font2.configure(**cfg)
spn.bind('<<Increment>>', size)
spn.bind('<<Decrement>>', size)
cbo.grid(row=0, column=1, sticky=tk.NSEW)
ent.grid(row=0, column=2, sticky=tk.NSEW)
lb_size.grid(row=0, column=3, sticky=tk.NSEW)
spn.grid(row=0, column=4, sticky=tk.NSEW)
frame.grid(sticky=tk.NSEW)
root.mainloop()
The only way I have found to avoid this issue is to change the font from each entry's configure() method. I guess the easier way will be to put all the entries in a list, see code below.
I have also noticed that size() uses the previous value of the spinbox not the one after the increment/decrement. To avoid that, I suggest you to use the command option of the spinbox instead of the bindings to <<Increment>> and <<Decrement>> (see code below).
Finally, you do not need to retrieve the whole font settings to update the size, you can simply do font1.configure(size=<newsize>).
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
root = tk.Tk()
root.rowconfigure(0, weight=1)
root.columnconfigure(99, weight=1)
frame = ttk.Frame(root)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
entries = [] # list of all entries
cbo = ttk.Combobox(frame)
cbo.config(
values=('Test 1', 'Test 2', 'Test 3')
)
cbo.set(value='Test 1')
entries.append(cbo)
ent_var = tk.StringVar()
ent = ttk.Entry(frame, textvariable=ent_var)
ent_var.set('Test')
entries.append(ent)
def size():
size = spn.get() # get current spinbox's value
font1.configure(size=size)
font2.configure(size=size)
for e in entries: # update font in all entries
e.configure(font=font2)
lb_size = ttk.Label(frame, text='size')
# use the command option to update the font size
spn = ttk.Spinbox(frame, command=size, values=tuple(range(1, 101)))
font1 = tkfont.nametofont('TkDefaultFont')
font2 = tkfont.nametofont('TkTextFont')
lbl_field_name = ttk.Label(frame, text='Field Name')
lbl_field_name.grid()
cbo.grid(row=0, column=1, sticky=tk.NSEW)
ent.grid(row=0, column=2, sticky=tk.NSEW)
lb_size.grid(row=0, column=3, sticky=tk.NSEW)
spn.grid(row=0, column=4, sticky=tk.NSEW)
frame.grid(sticky=tk.NSEW)
root.mainloop()

Tkinter: right align Labels within stretched LabelFrames using grid

Using grid in tkinter, I'm trying to align a set of frames (I would love to post a picture, but I'm not allowed.)
I've two outer LabelFrames of different sizes and on top of each other which I'd like to stretch and align.
Within the bottom frame, I've a stack of several other LabelFrames and within each of the LabelFrames there is a Label. I would like for the LabelFrames to extend as much as the outer container and for each of the inner Labels to be right align with respect to the containing LabelFrame.
I've tried, without success, with various combinations of sticky, anchor, justify.
Any suggestion, recommendation?
#!/usr/bin/env python
import Tkinter as tk
class AlignTest(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.grid()
self.parent.title('Align test')
self.createMenus()
self.createWidgets()
def createMenus(self):
# Menu
self.menubar = tk.Menu(self.parent)
self.parent.config(menu=self.menubar)
# Menu->File
self.fileMenu = tk.Menu(self.menubar)
# Menu->Quit
self.fileMenu.add_command(label='Quit',
command=self.onExit)
# Create File Menu
self.menubar.add_cascade(label='File',
menu=self.fileMenu)
def createWidgets(self):
# Main frame
self.mainFrame = tk.Frame(self.parent)
self.mainFrame.grid(row=0, column=0)
# Outer LabelFrame1
self.outerLabelFrame1 = tk.LabelFrame(self.mainFrame,
text='Outer1')
self.outerLabelFrame1.grid(row=0, column=0)
# Inner Label
self.innerLabel = tk.Label(self.outerLabelFrame1,
text='This is a longer string, for example!')
self.innerLabel.grid(row=0, column=0)
# Outer LabelFrame2
self.outerLabelFrame2 = tk.LabelFrame(self.mainFrame,
text='Outer2')
self.outerLabelFrame2.grid(row=1, column=0, sticky='ew')
# Inner labelFrames each with a single labels
self.innerLabel1 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner1')
self.innerLabel1.grid(row=0, column=0, sticky='ew')
self.value1 = tk.Label(self.innerLabel1,
bg='green',
text='12.8543')
self.value1.grid(row=0, column=0, sticky='')
self.innerLabel2 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner2')
self.innerLabel2.grid(row=1, column=0, sticky='ew')
self.value2 = tk.Label(self.innerLabel2,
bg='green',
text='0.3452')
self.value2.grid(row=0, column=0, sticky='')
self.innerLabel3 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner3')
self.innerLabel3.grid(row=2, column=0, sticky='')
self.value3 = tk.Label(self.innerLabel3,
bg='green',
text='123.4302')
self.value3.grid(row=0, column=0, sticky='')
def onExit(self):
self.parent.quit()
def main():
root = tk.Tk()
app = AlignTest(root)
app.mainloop()
if __name__ == '__main__':
main()
Without even running your code I see two problems. The first is that you aren't always using the sticky parameter when calling grid. That could be part of the problem. I've rarely ever used grid without using that parameter.
The second problem is that you aren't giving any of your rows and columns any weight. Without a positive weight, columns and rows will only ever use up exactly as much space as they need for their contents, and no more. Any extra space goes unallocated.
A good rule of thumb is that in every widget that is being used as a container for other widgets (typically, frames), you should always give at least one row and one column a positive weight.
As a final suggestion: during development it's really helpful to give each of your frames a distinctive color. This really helps to visualize how the frames are using the available space.
Thanks to Bryan's comment on weight, here is a working version of the code as potential reference. (I'll add pictures when allowed.)
#!/usr/bin/env python
import Tkinter as tk
class AlignTest(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.grid()
self.parent.title('Align test')
self.createMenus()
self.createWidgets()
def createMenus(self):
# Menu
self.menubar = tk.Menu(self.parent)
self.parent.config(menu=self.menubar)
# Menu->File
self.fileMenu = tk.Menu(self.menubar)
# Menu->Quit
self.fileMenu.add_command(label='Quit',
command=self.onExit)
# Create File Menu
self.menubar.add_cascade(label='File',
menu=self.fileMenu)
def createWidgets(self):
# Main frame
self.mainFrame = tk.Frame(self.parent)
self.mainFrame.grid(row=0, column=0)
# Outer LabelFrame1
self.outerLabelFrame1 = tk.LabelFrame(self.mainFrame,
text='Outer1')
self.outerLabelFrame1.grid(row=0, column=0)
# Inner Label
self.innerLabel = tk.Label(self.outerLabelFrame1,
text='This is a longer string, for example!')
self.innerLabel.grid(row=0, column=0)
# Outer LabelFrame2
self.outerLabelFrame2 = tk.LabelFrame(self.mainFrame,
text='Outer2')
self.outerLabelFrame2.grid(row=1, column=0, sticky='ew')
self.outerLabelFrame2.grid_columnconfigure(0, weight=1)
# Inner labelFrames each with a single labels
self.innerLabel1 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner1')
self.innerLabel1.grid(row=0, column=0, sticky='ew')
self.innerLabel1.grid_columnconfigure(0, weight=1)
self.value1 = tk.Label(self.innerLabel1,
bg='green',
anchor='e',
text='12.8543')
self.value1.grid(row=0, column=0, sticky='ew')
self.innerLabel2 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner2')
self.innerLabel2.grid(row=1, column=0, sticky='ew')
self.innerLabel2.grid_columnconfigure(0, weight=1)
self.value2 = tk.Label(self.innerLabel2,
bg='green',
anchor='e',
text='0.3452')
self.value2.grid(row=0, column=0, sticky='ew')
self.innerLabel3 = tk.LabelFrame(self.outerLabelFrame2,
bg='yellow',
text='Inner3')
self.innerLabel3.grid(row=2, column=0, sticky='ew')
self.innerLabel3.grid_columnconfigure(0, weight=1)
self.value3 = tk.Label(self.innerLabel3,
bg='green',
anchor='e',
text='123.4302')
self.value3.grid(row=0, column=0, sticky='ew')
def onExit(self):
self.parent.quit()
def main():
root = tk.Tk()
app = AlignTest(root)
app.mainloop()
if __name__ == '__main__':
main()

Categories

Resources