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()
Related
I am trying to get my scrollable canvas to work. It works when I pack the elements using .pack, however when I insert the elements via .place, the scrollbar stops working. Here is a minimal reproducable example of my code.
startup.py file:
import frame as f
import placeWidgetsOnFrame as p
p.populate3()
f.window.mainloop()
frame.py file:
#Creates widnow
window = customtkinter.CTk()
window.geometry("1900x980")
customtkinter.set_appearance_mode("dark")
window.resizable(False, False)
#Creates Frame for GUI
mainFrame = customtkinter.CTkFrame(window, width=1900, height=980, corner_radius=0)
mainFrame.pack(expand=True, fill=tk.BOTH)
mainFrame.pack_propagate(False)
topFrame = customtkinter.CTkFrame(master=mainFrame, width=1865, height=140, corner_radius=10)
topFrame.grid(columnspan=2, padx=15, pady=15)
topFrame.pack_propagate(0)
leftFrame = customtkinter.CTkFrame(master=mainFrame, width=380, height=530, corner_radius=10)
leftFrame.grid(row=1, column=0, padx=15, pady=10)
leftFrame.pack_propagate(False)
rightFrame = customtkinter.CTkFrame(master=mainFrame, width=1450, height=775, corner_radius=10)
rightFrame.grid(row=1, column=1, padx=15, pady=10, rowspan=2)
rightFrame.pack_propagate(False)
bottomLeftFrame = customtkinter.CTkFrame(mainFrame, width=380, height=220, corner_radius=10)
bottomLeftFrame.grid(row=2, column=0, padx=15, pady=10)
bottomLeftFrame.pack_propagate(False)
#Creates Scrollbar for right Frame
#Creates a canvas for the right Frame
canvas2=tk.Canvas(rightFrame, bg="#000000", highlightthickness=0, relief="flat")
canvas2.pack(side="left", fill="both", expand=True)
#Creates a scroll bar for the right Frame
scrollbar = customtkinter.CTkScrollbar(master=rightFrame, orientation="vertical", command=canvas2.yview, corner_radius=10)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
#Configures scrollbar to canvas
canvas2.configure(yscrollcommand=scrollbar.set)
canvas2.bind("<Configure>", lambda *args, **kwargs: canvas2.configure(scrollregion=canvas2.bbox("all")))
#Creates a scrollable frame to place widgets on
scrollableFrame = customtkinter.CTkFrame(canvas2, fg_color=("#C0C2C5", "#343638"), corner_radius=10)
canvasFrame = canvas2.create_window((0,0), window=scrollableFrame, anchor="nw", tags=("cf"))
#TO DO - resize canvas and to fit all widgets
def handleResize(event):
c = event.widget
cFrame = c.nametowidget(c.itemcget("cf", "window"))
minWidth = cFrame.winfo_reqwidth()
minHeight = cFrame.winfo_reqheight()
print (event.width)
print (event.height)
if minWidth < event.width:
c.itemconfigure("cf", width=event.width)
if minHeight < event.height:
c.itemconfigure("cf", height=event.height)
print (event.width)
print (event.height)
c.configure(scrollregion=c.bbox("all"))
canvas2.bind('<Configure>', handleResize)
def onMousewheel(event):
canvas2.yview_scroll(-1 * round(event.delta / 120), "units")
canvas2.bind_all("<MouseWheel>", onMousewheel)
canvas2.bind("<Destroy>", lambda *args, **kwargs: canvas2.unbind_all("<MouseWheel>"))
placeWidgetsOnFrame.py file:
import tkinter
import customtkinter
import frame as f
rightFrame = f.scrollableFrame
def populate2():
for i in range(30):
emailLabel = customtkinter.CTkLabel(master=rightFrame, text="Please enter your email:")
emailLabel.pack(padx=10, pady=10)
def populate3():
x=50
for i in range(30):
emailLabel = customtkinter.CTkLabel(master=rightFrame, text="Please enter your email:")
emailLabel.place(x=40, y=x)
x=x+50
Here is the output when populate3() is run:
Here
Here is the output when populate2() is run
Here
Does anyone know why this is? I can always go back and change the way I insert widgets to .pack rather than .place, however I would rather use .place as I find it easier to place widgets where I want to.
The reason is because pack by default will cause the containing frame to grow or shrink to fit all of the child widgets, but place does not. If your frame starts out as 1x1 and you use place to add widgets to it, the size will remain 1x1. When you use place, it is your responsibility to make the containing widget large enough to contain its children.
This single feature is one of the most compelling reasons to choose grid or pack over place - these other geometry managers do a lot of work for you so that you can think about the layout logically without getting bogged down in the details of the layout.
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)))
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 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
I'm attempting to resize a canvas so the canvas fills the entire window when the window is resized by the user employing a 'click and drag' on the window edge, but I am not successful.
I have looked at the following other questions:
Question #1 (previously posted, but not helping here)
This one sent me in what I thought was the correct direction because I bind a Configure on canvas. However, I get an error that " 'canvas' is not defined in FrameWidth."
Tkinter: How to get frame in canvas window to expand to the size of the canvas?
If I modify the function call and the function, then I get the error that "TypeError: FrameWidth() missing 1 required positional argument: 'canvas' " The modification is
canvas.bind('<Configure>', self.FrameWidth(canvas))
def FrameWidth(self, event, canvas):
Question #2 (previously posted, but not helping here)
I also looked at this question:
Python Tkinter Scrollbar Shaky Scrolling
But this question is addressing an erratically behaving scrollbar.
Question #3 (previously posted, but not helping here)
I have also looked at this questions and put in weight=1, but that is not helping:
Python TKinter: frame and canvas will expand horizontally, but not vertically, when window is resized
Any advice would be appreciated.
Here's my MWE:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 3 04:39:43 2017
#author: davidc
"""
import tkinter as tk
class Selections(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.FifthLabelLeft = tk.Label(self,
text="""Riding""",
justify = tk.CENTER,
width=20,
padx = 10).grid(row=4, column = 0, pady=5)
self.FifthLabelCenter = tk.Label(self,
text="""Winning Candidate""",
justify = tk.CENTER,
width=20,
padx = 10).grid(row=4, column = 1, pady=5)
self.FifthLabelRight = tk.Label(self,
text="""Percent of Vote""",
justify = tk.CENTER,
width=10,
padx = 10).grid(row=4, column = 2, pady=5)
mybox = tk.LabelFrame(self, padx=5, pady=4)
mybox.grid(row=5, column=0, columnspan=3)
canvas = tk.Canvas(mybox, borderwidth=5, background="#70ff33")
frame = tk.Frame(canvas, background="#33f4ff")
vsb = tk.Scrollbar(mybox, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, heigh=30)
mybox.grid_columnconfigure(5,weight=1)
mybox.grid_rowconfigure(5,weight=1)
frame.grid_columnconfigure(5,weight=1)
frame.grid_rowconfigure(5,weight=1)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event: self.OnFrameConfigure(canvas))
canvas.bind('<Configure>', self.FrameWidth)
self.fillWindow(frame)
self.QuitButton = tk.Button(self,
text="QUIT",
command=root.destroy,
padx=25, pady=0)
self.QuitButton.grid(column = 0, columnspan=3)
def fillWindow(self, frame):
PartyWinnersList = [['Some list of places', "Somebody's Name", 0.37448599960838064],
['A shorter list', 'Three Long Names Here', 0.52167817821240514],
['A much longer, longer entry', 'Short Name', 0.41945832387008858]]
placement = 2
for i in PartyWinnersList:
ShowYear = tk.Label(frame,
text="""%s """ % i[0],
width=20,
)
ShowYear.grid(row=placement, column = 0, sticky=tk.S)
ShowSystem = tk.Label(frame,
text="""%s """ % i[1],
width=20,
)
ShowSystem.grid(row=placement, column = 1, sticky=tk.N)
PercentVotes = i[2]*100
ShowVotes = tk.Label(frame,
text="""%3.1f""" % PercentVotes,
width=10,
)
ShowVotes.grid(row=placement, column = 2, sticky=tk.N)
placement += 1
def FrameWidth(self, event):
canvas_width = event.width
canvas.itemconfig(self.canvas_frame, width = canvas_width)
def OnFrameConfigure(self, canvas):
canvas.configure(scrollregion=canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
main = Selections(root)
main.pack(side="top", fill="both", expand=True)
root.mainloop()
No need to reinvent the wheel here. Canvas() is a tkinter widget, and like all tkinter widgets it has to be drawn in the window using a geometry manager. This means that we can manipulate how it appears in the window.
Example program below modified from an example found on this page.
If we use .pack() then we can do something like the below:
from tkinter import *
root = Tk()
w = Canvas(root, width=200, height=100)
w.pack(fill="both", expand=True)
w.create_line(0, 0, 200, 100)
w.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))
w.create_rectangle(50, 25, 150, 75, fill="blue")
root.mainloop()
Where the combination of fill="both" and expand=True tells the widget to eat up all the extra space it's given in the window and expand to fill it.
If we're using .grid() then we have to do something slightly different:
from tkinter import *
root = Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
w = Canvas(root, width=200, height=100)
w.grid(sticky=N+S+E+W)
w.create_line(0, 0, 200, 100)
w.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))
w.create_rectangle(50, 25, 150, 75, fill="blue")
root.mainloop()
Here we use .rowconfigure() and .columnconfigure to tell row 0 and column 0 (where our canvas is) to have a higher weight than any other row or column, meaning they get given more of the free space in the window than the others (In this case all of it seeing as how no other rows or columns exist), we then need to tell the widget to actually expand with the "cell" it resides in, which we do by specifying sticky=N+S+E+W, which tells the widget to stick to all 4 edges of the cell when they expand, thus expanding the widget.