Tkinter text widget deforms column size - python

I can't pack widgets in rows or columns as in image, can you help me?
The problem is text widget deforms column size, text should not be in row=3,column=0 ?
def _formato(self):
t1=tkinter.Toplevel(self._finestra)
labelTop = tkinter.Label(t1,text = "Tipo di carattere")
labelTop.grid(row=0, column=0)
labelTop2 = tkinter.Label(t1,text = "Dimensione")
labelTop2.grid(row=0, column=1)
labelTop3 = tkinter.Label(t1)
labelTop3.grid(row=2, column=0)
listaFont=tkinter.ttk.Combobox(t1)
allfonts = sorted(tkinter.font.families())
listaFont["values"] = allfonts
listaFont.grid(row=1, column=0)
listaFont.bind("<<ComboboxSelected>>", None)
listaDimensione = tkinter.ttk.Combobox(t1)
allfontsizes = list(range(8,70))
listaDimensione['values'] = allfontsizes
listaDimensione.grid(row=1, column=1)
testo= tkinter.Text(t1)
testo.insert(tkinter.INSERT,'AaBbYyZz')
testo.grid(row=3,column=0)

Question: All widgets in the same column should have equal width.
The core is, use a Frame for every column and layout the widgets into the Frame.
This allows all widgets to resize to the Frame width.
Define class App for demonstration purpose
class App(tk.Tk):
def __init__(self):
super().__init__()
To get equal width use a tk.Frame for every column.
Allow the Frame to grow his width.
Allow the widgets inside the Frame to grow his width.
Define the Frame to grow up to the App width.
# column 0
self.grid_columnconfigure(0, weight=1)
frame_0 = tk.Frame(self)
frame_0.grid_columnconfigure(0, weight=1)
frame_0.grid(row=0, column=0, sticky='nsew')
Add the widgets ...
Define every widget to grow his width up to the Frame width.
labelTop = tkinter.Label(frame_0, text="Tipo di carattere")
labelTop.grid(row=0, column=0, sticky='ew')
listaFont = tkinter.ttk.Combobox(frame_0)
listaFont.grid(row=1, column=0, sticky='ew')
allfonts = sorted(tkinter.font.families())
listaFont["values"] = allfonts
listaFont.bind("<<ComboboxSelected>>", None)
Note: Reset the default using width=1
testo = tkinter.Text(frame_0, width=1)
testo.insert(tkinter.INSERT, 'AaBbYyZz')
testo.grid(row=3, column=0, sticky='ew')
The same for columen 1 ...
# column 1
self.grid_columnconfigure(1, weight=1)
frame_1 = tk.Frame(self)
frame_1.grid_columnconfigure(0, weight=1)
frame_1.grid(row=0, column=1, sticky='nsew')
labelTop2 = tkinter.Label(frame_1, text="Dimensione")
labelTop2.grid(row=0, column=0, sticky='ew')
listaDimensione = tkinter.ttk.Combobox(frame_1)
allfontsizes = list(range(8, 70))
listaDimensione['values'] = allfontsizes
listaDimensione.grid(row=1, column=0, sticky='ew')
Usage:
if __name__ == "__main__":
App().mainloop()
Tested with Python: 3.5

Related

Blank page when removing all mentions of grid()

I've switched from .grid() to .place() in my program, so I decided to remove a frame that contained the grid widgets:
BackButtonR = Button(registerPage, text="Back", command=lambda: show_frame(Menu))
BackButtonR.grid(row=0, column=0, sticky=W)
Button2F3 = Button(registerPage, text="Find")
Button2F3.grid(row=1, column=1)
Button3F3 = Button(registerPage, text="Calculate").grid(row=6, column=1)
LabelTitleF3 = Label(registerPage, text="Calculate Buy Price").grid(row=0, column=3)
label1F3 = Label(registerPage, text="Enter Ticker Symbol:").grid(row=1, column=0)
label2F3 = Label(registerPage, text="Expected CAGR").grid(row=2, column=0)
label3F3 = Label(registerPage, text="Years of Analysis").grid(row=3, column=0)
label4F3 = Label(registerPage, text="Expected PE Ratio").grid(row=4, column=0)
label5F3 = Label(registerPage, text="Desired Annual Return").grid(row=5, column=0)
entry1F3 = Entry(registerPage, width=7).grid(row=1, column=1, padx=0)
entry2F3 = Entry(registerPage).grid(row=2, column=1, pady=10, padx=0)
entry3F3 = Entry(registerPage).grid(row=3, column=1, pady=10, padx=0)
entry4F3 = Entry(registerPage).grid(row=4, column=1, pady=10, padx=0)
entry5F3 = Entry(registerPage).grid(row=, column=1, pady=10, padx=0)
But weirdly, when I rerun my program everything turns blank. This shouldn't happen, since I've removed any reference to .grid(), so the program should be working fine with .place(). Here is my full code:
print(220+135)
from tkinter import *
root = Tk()
root.title("Account Signup")
DarkBlue = "#2460A7"
LightBlue = "#B3C7D6"
root.geometry('350x230')
Menu = Frame(root)
loginPage = Frame(root)
registerPage = Frame(root)
for AllFrames in (Menu, loginPage, registerPage):
AllFrames.grid(row=0, column=0, sticky='nsew')
AllFrames.configure(bg=LightBlue)
def show_frame(frame):
frame.tkraise()
show_frame(Menu)
# ============= Menu Page =========
Menu.grid_columnconfigure(0, weight=1)
menuTitle = Label(Menu, text="Menu", font=("Arial", 25), bg=LightBlue)
menuTitle.place(x=130, y=25)
loginButton1 = Button(Menu, width=25, text="Login", command=lambda: show_frame(loginPage))
loginButton1.place(x=85, y=85)
registerButton1 = Button(Menu, width=25, text="Register", command=lambda: show_frame(registerPage))
registerButton1.place(x=85, y=115)
# ======== Login Page ===========
loginUsernameL = Label(loginPage, text='Username').place(x=30, y=60)
loginUsernameE = Entry(loginPage).place(x=120, y=60)
loginPasswordL = Label(loginPage, text='Password').place(x=30, y=90)
loginPasswordE = Entry(loginPage).place(x=120, y=90)
backButton = Button(loginPage, text='Back', command=lambda: show_frame(Menu)).place(x=0, y=0)
loginButton = Button(loginPage, text='Login', width=20).place(x=100, y=150)
# ======== Register Page ===========
root.mainloop()
Why is my program turning blank?
When you use pack and grid, these functions will normally adjust the size of a widget's parent to fit all of its children. It's one of the most compelling reasons to use these geometry managers.
When you use place this doesn't happen. If you use place to put a widget in a frame, the frame will not grow or shrink to fit the widget.
In your case you're creating Menu, loginPage and registerPage and not giving them a size so they default to 1x1 pixels. When you use place to add a widget to the frame, the frame will remain at 1x1 pixels, rendering it virtually invisible.
The solution is to either give these frames an explicit size, or add the frames to the window with options that cause them to fill the window.
For illustrative purposes I've changed the background color of the window to pink, and set the size of Menu to 200x200. As you can see in the following screenshot, the frame with the widgets is there, and becomes visible when you give it a larger size. Of course, one problem with place is it's up to you to calculate the appropriate size.
The better solution in this specific case would be to use the appropriate grid options to have the frames fill the window. You can do that by giving a weight to the row and column that the frames are in. Unused space in the parent frame will be allocated to the row and column with the widget.
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Generally speaking, grid and pack are superior to place for implementing most layouts because they are able to automatically make all widgets fit into a window with very little work. With place it's up to you to do calculations for position and size, and to make sure that all ancestors are appropriately sized and are visible.
You need to call root.grid_rowconfigure(0, weight=1) and root.grid_columnconfigure(0, weight=1) so that the shown frame use all the space of root window, otherwise the size of those frames are 1x1.
Also Menu.grid_columnconfigure(0, weight=1) is useless because widgets inside Menu are using .place().

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

Resizing the Canvas equal to the frame size in tkinter python

I have two questions related to this attached code. This code is a part of my project in which i have to manage the attendance of 50 (or more) students.
When you will run this piece of code, you will see that there is a extra white space (that might be of the canvas) inside the Label Frame i.e. Attendance_Frame. All I wanted is that the there should be no extra white space and the scrollbar, instead of being at the extreme right, should be at the place where the labels end.
I have searched for the answer to my question and saw a similar case. But there, the person wanted the frame to expand to the canvas size. Link (Tkinter: How to get frame in canvas window to expand to the size of the canvas?).
But in my case, I want the canvas size to be equal to frame size (although the frame lies inside the canvas)
The other thing I want is that all the check boxes should initially be 'checked' (showing the present state) and when I uncheck random checkboxes (to mark the absent), and click the 'Submit' button (yet to be created at the bottom of the window), I should get a list with 'entered date' as first element and the roll numbers i.e. 2018-MC-XX as other elements. For example : ['01/08/2020', '2018-MC-7', '2018-MC-11', '2018-MC-23', '2018-MC-44'].
Actually my plan is when i will get a list i will easily write it to a text file.
from tkinter import *
from tkcalendar import DateEntry
root = Tk()
root.geometry('920x600+270+50')
root.minsize(920,600)
Attendance_frame = Frame(root) ### Consider it a Main Frame
Attendance_frame.pack()
attendaceBox = LabelFrame(Attendance_frame, text = 'Take Attendance', bd = 4, relief = GROOVE, labelanchor = 'n',font = 'Arial 10 bold', fg = 'navy blue', width = 850, height = 525) # A Label Frame inside the main frame
attendaceBox.pack_propagate(0)
attendaceBox.pack(pady = 15)
dateFrame = Frame(attendaceBox) # A small frame to accommodate date entry label & entry box
dateFrame.pack(anchor = 'w')
font = 'TkDefaultFont 10 bold'
date_label = Label(dateFrame, text = 'Enter Date : ', font = font).grid(row = 0, column = 0, sticky = 'w', padx = 10, pady = 10)
date_entry = DateEntry(dateFrame, date_pattern = 'dd/mm/yyyy', showweeknumbers = FALSE, showothermonthdays = FALSE)
date_entry.grid(row = 0, column = 1, sticky = 'w')
noteLabel = Label(attendaceBox, text = 'Note: Uncheck the boxes for absentees').pack(anchor = 'w', padx = 10, pady = 5)
canvas = Canvas(attendaceBox, borderwidth=0, background="#ffffff")
checkFrame = Frame(canvas, width = 100, height = 50)
vsb = Scrollbar(canvas, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.pack_propagate(0)
canvas.create_window((4,4), window=checkFrame, anchor="nw")
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
checkFrame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
for i in range(0,51): # A loop to create Labels of students roll numbers & names
c = Checkbutton(checkFrame, text = f"{'2018-MC-'+str(i+1)} Student {i+1}")
c.grid(row = i, column = 0, padx = 10, sticky = 'w')
mainloop()
If you are creating a vertical list of items, you don't need to use a frame inside the canvas. The inner frame adds some unnecessary complexity. Instead, create the checkbuttons directly on the canvas with create_window.
You also need to configure the scrollregion attribute so that the scrollbar knows how much of the virtual canvas to scroll.
Finally, to have them selected you should assign a variable to each checkbutton, and make sure that the value is the proper value. By default checkbuttons use the values 0 and 1, so setting the variable to 1 will make it selected.
vars = []
for i in range(0,51):
var = IntVar(value=1)
vars.append(var)
x0, y0, x1, y1 = canvas.bbox("all") or (0,0,0,0)
c = Checkbutton(canvas, text = f"{'2018-MC-'+str(i+1)} Student {i+1}", variable=var)
canvas.create_window(2, y1+4, anchor="nw", window=c)
canvas.configure(scrollregion=canvas.bbox("all"))
Your all questions answer is here:
You should try this.
import tkinter as tk
from tkcalendar import DateEntry
root = tk.Tk()
root.geometry('920x600+270+50')
root.minsize(960, 600)
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
Attendance_frame = tk.Frame(root)
Attendance_frame.grid(row=0, column=0, sticky='nsew')
attendaceBox = tk.LabelFrame(Attendance_frame,
text='Take Attendance',
bd=4,
relief='groove',
labelanchor='n',
font='Arial 10 bold',
fg='navy blue',
width=850,
height=525)
attendaceBox.grid(row=0, column=0, sticky='nsew', padx=15)
Attendance_frame.columnconfigure(0, weight=1)
Attendance_frame.rowconfigure(0,weight=1)
dateFrame = tk.Frame(attendaceBox) # A small frame to accommodate date entry label & entry box
dateFrame.grid(row=0, column=0, sticky='nsew')
font = 'TkDefaultFont 10 bold'
date_label = tk.Label(dateFrame, text='Enter Date : ', font=font)
date_label.grid(row=0, column=0, sticky='w', padx=10, pady=10)
date_entry = DateEntry(dateFrame, date_pattern='dd/mm/yyyy', showweeknumbers=False, showothermonthdays=False)
date_entry.grid(row=0, column=1, sticky='w')
noteLabel = tk.Label(attendaceBox, text='Note: Uncheck the boxes for absentees', anchor='w')
noteLabel.grid(row=1, column=0, sticky='nsew')
attendaceBox.rowconfigure(2, weight=1)
canvas = tk.Canvas(attendaceBox, width=200, borderwidth=0, background="#ffffff")
# You can set width of canvas according to your need
canvas.grid(row=2, column=0, sticky='nsew')
canvas.columnconfigure(0, weight=1)
canvas.rowconfigure(0, weight=1)
vsb = tk.Scrollbar(attendaceBox, orient="vertical", command=canvas.yview)
vsb.grid(row=2, column=1, sticky='nsew')
canvas.configure(yscrollcommand=vsb.set)
checkFrame = tk.Frame(canvas, bg='green')
canvas.create_window((0, 0), window=checkFrame, anchor="nw", tags='expand')
checkFrame.columnconfigure(0, weight=1)
for i in range(0, 51): # A loop to create Labels of students roll numbers & names
c = tk.Checkbutton(checkFrame, anchor='w', text=f"{'2018-MC-' + str(i + 1)} Student {i + 1}")
c.grid(row=i, column=0, sticky='nsew')
c.select()
canvas.bind('<Configure>', lambda event: canvas.itemconfigure('expand', width=event.width))
checkFrame.update_idletasks()
canvas.config(scrollregion=canvas.bbox('all'))
root.mainloop()
Get Selected Value
vars = []
for i in range(0, 51): # A loop to create Labels of students roll numbers & names
var = tk.IntVar()
c = tk.Checkbutton(checkFrame,
variable=var,
anchor='w', text=f"{'2018-MC-' + str(i + 1)} Student {i + 1}")
c.grid(row=i, column=0, sticky='nsew')
c.select()
vars.append(var)
def state():
print(list(map((lambda var: var.get()), vars)))

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