How to display newly added tkinter widgets - python

I am using python3 tkinter. I am trying to update a table to dynamically add new lines of Entry widgets.
The layout is as follows:
frame_canvas include a canvas and a scrollbar
frame_entries is on the canvas, showing 5x7 windows only
frame_entries holds a 2-dimensional Entry table
I'd like to dynamically add new rows on the Entry table, thus, adding new Entry rows on frame_entries frame.
The problem now is that the addition of these new widgets won't update the widgets display, it would only replace the last row of Entry table on the screen. (data was updated correctly on the background, just the problem of display update)
I tried to call canvas.update() and frame_entries.update() but no use. Scrollbar doesn't reflect the updated Entry table either.
I updated the code snippet so you can download and try.
import tkinter as tk
total_columns = 5
total_rows = 18
tbl = []
def add_row_table():
global total_rows
new_row: list[Entry] = [tk.Entry() for _ in range(total_columns)]
for j in range(total_columns):
new_row[j] = tk.Entry(frame_entries, width=20)
new_row[j].grid(row=total_rows, column=j, sticky="news")
new_row[j].insert(tk.END, total_rows)
total_rows += 1
# canvas.update_idletasks()
# frame_entries.update_idletasks()
tbl.append(new_row)
root = tk.Tk()
root.grid_rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
frame_main = tk.Frame(root, bg="gray")
frame_main.grid(sticky='news')
button2 = tk.Button(frame_main, text="Add Row", fg="green", command=add_row_table)
button2.grid(row=0, column=1, pady=(5, 0), sticky='nw')
frame_canvas = tk.Frame(frame_main)
frame_canvas.grid(row=2, column=0, pady=(5, 0), sticky='nw')
frame_canvas.grid_rowconfigure(0, weight=1)
frame_canvas.grid_columnconfigure(0, weight=1)
# Set grid_propagate to False to allow table resizing later
frame_canvas.grid_propagate(False)
canvas = tk.Canvas(frame_canvas, bg="yellow")
canvas.grid(row=0, column=0, sticky="news")
vsb = tk.Scrollbar(frame_canvas, orient="vertical", command=canvas.yview)
vsb.grid(row=0, column=1, sticky='ns')
canvas.configure(yscrollcommand=vsb.set)
frame_entries = tk.Frame(canvas, bg="blue")
canvas.create_window((0, 0), window=frame_entries, anchor='nw')
for i in range(total_rows):
line = [tk.Entry() for j in range(total_columns)]
for j in range(total_columns):
line[j] = tk.Entry(frame_entries, width=20)
line[j].grid(row=i, column=j, sticky="news")
line[j].insert(tk.END, i)
tbl.append(line)
frame_entries.update_idletasks()
columns_width = sum([tbl[0][j].winfo_width() for j in range(total_columns)])
rows_height = sum([tbl[i][0].winfo_height() for i in range(5)])
frame_canvas.config(width=columns_width + vsb.winfo_width(),
height=rows_height+150)
canvas.config(scrollregion=canvas.bbox("all"))
root.mainloop()

You need to update scrollregion of canvas after adding entries to frame_entries:
...
# function to be called whenever frame_entries is resized
def frame_entries_resized(event):
# update canvas scrollregion
canvas.config(scrollregion=canvas.bbox('all'))
...
frame_entries = tk.Frame(canvas, bg="blue")
# monitor size of frame_entries
frame_entries.bind('<Configure>', frame_entries_resized)
canvas.create_window((0, 0), window=frame_entries, anchor='nw')
...
Note that you have created duplicate set of entries:
for i in range(total_rows):
# initialise with entries into 'line'
line = [tk.Entry() for j in range(total_columns)]
# then assign new entry into 'line' again
for j in range(total_columns):
line[j] = tk.Entry(frame_entries, width=20)
line[j].grid(row=i, column=j, sticky="news")
line[j].insert(tk.END, i)
tbl.append(line)
You can initialize line as list of None instead:
line = [None for j in range(total_columns)]
Same issue inside add_row_table():
def add_row_table():
global total_rows
# should initialise new_row as list of None instead
new_row: list[Entry] = [tk.Entry() for _ in range(total_columns)]
for j in range(total_columns):
new_row[j] = tk.Entry(frame_entries, width=20)
new_row[j].grid(row=total_rows, column=j, sticky="news")
new_row[j].insert(tk.END, total_rows)
total_rows += 1
# canvas.update_idletasks()
# frame_entries.update_idletasks()
tbl.append(new_row)

Related

Tkinter: how can i get frame in canvas to expand to the size of the canvas?

i've been trying to get this tkinter frame to fill to the side, i've tried adding in expand but that would screw up the scrollbar
Why wont this fill to the right?
This is the parent frame
self.frame_right.rowconfigure(0, weight=2)
self.frame_right.rowconfigure(1, weight=8)
self.frame_right.columnconfigure((0, 1), weight=1)
self.frame_right.columnconfigure(2, weight=0)
self.frame_info = customtkinter.CTkFrame(master=self.frame_right)
self.frame_info.grid(row=0, column=0, columnspan=2, rowspan=4, pady=20, padx=20, sticky="nsew")
# ============ frame_info ============
self.frame_info.grid_columnconfigure(1, weight=1)
self.frame_info.grid_columnconfigure(0, weight=1)
self.frame_info.grid_rowconfigure(1, weight=1)
This is the code that creates the layout
def checkhotel(self, hotel1, hotel2):
if hotel1 == "Select a hotel" or hotel2 == "Select a hotel":
errormessage(self, "Hotel not selected")
elif hotel1 not in allHotels or hotel2 not in allHotels:
errormessage(self, "Hotel does not exist in database")
else:
self.graphcanvas = customtkinter.CTkCanvas(master=self.frame_info, background="#333333", highlightthickness=0)
self.graphcanvas.grid(row=1, column=0, columnspan=4, padx=20, pady=20, sticky="nsew")
self.ctk_scrollbar = customtkinter.CTkScrollbar(master=self.frame_info, command=self.graphcanvas.yview,
scrollbar_color="white", background="grey")
self.ctk_scrollbar.grid(row=1, column=4, sticky="ns")
# connect textbox scroll event to CTk scrollbar
self.graphcanvas.configure(yscrollcommand=self.ctk_scrollbar.set)
self.frame_graphs = customtkinter.CTkFrame(master=self.graphcanvas)
self.secondframeid = self.graphcanvas.create_window((0, 0), window=self.frame_graphs, anchor="nw")
self.graphcanvas.bind("<Configure>",
lambda event: self.graphcanvas.configure(scrollregion=self.graphcanvas.bbox("all")))
#expand window
getcompreddata(self, hotel1, hotel2)
i've tried using column span and expand for binding too, however, it screws up the scrollbar
def plt_freq_neg_word(self, dataframe, hotelcolumn):
# highest negative sentiment reviews (with more than 5 words)
NEG_BE_reviews_df = dataframe[dataframe["nb_words"] >= 5].sort_values("neg", ascending=False)[
["review_clean", "neg"]].head()
all_words = ' '.join([word for word in NEG_BE_reviews_df['review_clean']])
tokenized_words = nltk.tokenize.word_tokenize(all_words)
words = nltk.word_tokenize(all_words)
fd = FreqDist(words)
top_10 = fd.most_common(10)
negativeFig = plt.Figure(figsize=(5, 4), dpi=75)
ax2 = negativeFig.add_subplot(111)
line2 = FigureCanvasTkAgg(negativeFig, self.frame_graphs)
line2.get_tk_widget().grid(row=4, column=hotelcolumn, padx=10, pady=10, columnspan=2)
fdist = pd.Series(dict(top_10))
sns.set_theme(style="ticks", )
sns.barplot(y=fdist.index, x=fdist.values, color='green', ax=ax2).set(
title="Top 10 Most Frequent word from Negative Review")
Thanks in advance

TreeView width unexpectedly changes

I have a Frame that contains Treeview and a Frame-embedded-in-a-Canvas.
Treeview displays documents from MongoDB in one '#0' column and every time a user selects any document I generate from document's fields a list of Checkbuttons in the Frame-embedded-in-a-Canvas.
The issue I got is that Treeview width is not constant, but I just can't understand why it changes and when exactly it happens.
Every time after generating new Checkbuttons list my Treeview width grows by 30 pixels without any regard to the fact that both the Treeview and the Frame-embedded-in-a-Canvas are expected to be in parent's grid columns 0 and 1 respectively with zero weight.
Here's a link to the GIF showing how it looks. And below you can find my code concerning these widgets.
Show me please where to look for this issue reason.
def treeview_reload():
dt_tv.delete(*dt_tv.get_children())
dt_tv.insert('', 'end', '_b_', text='biomes')
dt_tv.insert('', 'end', '_z_', text='zones')
dt_tv.insert('', 'end', '_p_', text='points')
dt_tv.insert('', 'end', '_m_', text='biomaterials')
dt_tv.insert('', 'end', '_c_', text='characters')
def tv_sel_handler(event):
try:
sel_iid = event.widget.selection()[0]
except IndexError:
sel_iid = ''
try:
generate_model(MAP[sel_iid])
except KeyError:
pass
def generate_model(document, new_list=True, row=0, indent=0):
if new_list:
for c in list(dt_model.children.values()):
c.destroy()
i = row
for field in document.keys():
if type(document[field]) is list:
cb = ttk.Label(dt_model, text=indent*2*' '+field)
cb.grid(row=i, column=0, sticky='w', padx=30, pady=4)
cb['font'] = ("Noto Mono", 13)
else:
cb = ttk.Checkbutton(dt_model, text=indent*2*' '+field)
cb.grid(row=i, column=0, sticky='w')
ttk.Style().configure('Mono.TCheckbutton', font=("Noto Mono", 13))
cb['style'] = 'Mono.TCheckbutton'
if type(document[field]) is dict:
i = generate_model(document[field], False, i+1, indent+1)
if i > 17:
vsb = ttk.Scrollbar(pdb_data, orient=VERTICAL, command=dt_canv.yview)
dt_canv['yscrollcommand'] = vsb.set
vsb.grid(row=0, column=2, sticky='nsw')
dt_model.bind('<Configure>',
lambda event, canvas=dt_canv:
canvas.configure(scrollregion=canvas.bbox("all")))
dt_model.bind('<Enter>',
lambda event:
dt_canv.bind_all("<MouseWheel>",
lambda event:
dt_canv.yview_scroll(int(-1*(event.delta/120)), "units")))
dt_model.bind('<Leave>',
lambda event: dt_canv.unbind_all("<MouseWheel>"))
return i
# ...
# ___ frame for the data
pdb_data = ttk.Frame(page_db, width=1250, height=450)
pdb_data.grid(row=6, column=0, columnspan=2, sticky='nsew', pady=24)
# ____ categorized documents list
dt_tv = ttk.Treeview(pdb_data, selectmode='browse', show=('tree',))
dt_tv.column('#0', width=250)
dt_tv.grid(row=0, column=0, sticky='nsw')
dt_tv.bind('<<TreeviewSelect>>', tv_sel_handler)
treeview_reload()
# ____ auxiliary canvas
dt_canv = Canvas(pdb_data, borderwidth=0, width=200)
dt_canv.grid(row=0, column=1, sticky='nsw', padx=5)
# _____ frame-embedded-in-a-canvas for checkbuttons list
dt_model = ttk.Frame(pdb_data, takefocus=0)
dt_canv.create_window((0,0), window=dt_model, anchor='nw')
pdb_data.rowconfigure(0, weight=1)
I have found a way how to prevent unexpected Treeview column width change. My solution is to remove Treeview from grid, then explicitly set column width and once again grid Treeview. The code is below.
But couldn't understand a reason of such behaviour. Any explanation is appreciated.
def treeview_reload():
dt_tv.delete(*dt_tv.get_children())
dt_tv.insert('', 'end', '_b_', text='biomes')
dt_tv.insert('', 'end', '_z_', text='zones')
dt_tv.insert('', 'end', '_p_', text='points')
dt_tv.insert('', 'end', '_m_', text='biomaterials')
dt_tv.insert('', 'end', '_c_', text='characters')
def tv_sel_handler(event):
try:
sel_iid = event.widget.selection()[0]
except IndexError:
sel_iid = ''
try:
generate_model(MAP[sel_iid])
except KeyError:
pass
def generate_model(document, new_list=True, row=0, indent=0):
# do not destroy Checkbuttons if recursive call
if new_list:
for c in list(dt_model.children.values()):
c.destroy()
i = row
for field in document.keys():
if type(document[field]) is list:
cb = ttk.Label(dt_model, text=indent*2*' '+field, font=("Noto Mono", 13))
cb.grid(row=i, column=0, sticky='w', padx=30, pady=4)
else:
ttk.Style().configure('Mono.TCheckbutton', font=("Noto Mono", 13))
cb = ttk.Checkbutton(dt_model,
text=indent*2*' '+field,
style='Mono.TCheckbutton')
cb.grid(row=i, column=0, sticky='w')
if type(document[field]) is dict:
i = generate_model(document[field], False, i+1, indent+1)
# only for recursive call
if not new_list:
return i
# this is here because Treeview width changes if grid only once
dt_tv.grid_remove()
dt_tv.column('#0', width=220)
dt_tv.grid(row=0, column=0, sticky='nsw')
# place vertical Scrollbar if needed and connect MouseWheel
if i > dt_canv.winfo_height() // 28:
vsb.grid(row=0, column=2, sticky='nsw')
dt_model.bind('<Configure>',
lambda event, canvas=dt_canv:
canvas.configure(scrollregion=canvas.bbox("all")))
dt_model.bind('<Enter>',
lambda event:
dt_canv.bind_all("<MouseWheel>",
lambda event:
dt_canv.yview_scroll(int(-1*(event.delta/120)), "units")))
dt_model.bind('<Leave>',
lambda event: dt_canv.unbind_all("<MouseWheel>"))
else:
dt_canv.unbind_all('<MouseWheel>')
dt_model.unbind_all('<Configure>')
vsb.grid_remove()
# ...
# ___ frame for the data
pdb_data = ttk.Frame(page_db, width=1250, height=450)
pdb_data.grid(row=6, column=0, columnspan=2, sticky='nsew', pady=24)
# ____ categorized documents list
dt_tv = ttk.Treeview(pdb_data, selectmode='browse', show=('tree',))
dt_tv.column('#0', width=250)
dt_tv.grid(row=0, column=0, sticky='nsw')
dt_tv.bind('<<TreeviewSelect>>', tv_sel_handler)
treeview_reload()
# ____ auxiliary canvas
dt_canv = Canvas(pdb_data, borderwidth=0, width=200)
dt_canv.grid(row=0, column=1, sticky='nsw', padx=5)
# _____ frame-embedded-in-a-canvas for checkbuttons list
dt_model = ttk.Frame(pdb_data, takefocus=0)
dt_canv.create_window((0,0), window=dt_model, anchor='nw')
# ____ vertical scrollbar for frame
vsb = ttk.Scrollbar(pdb_data, orient=VERTICAL, command=dt_canv.yview)
dt_canv['yscrollcommand'] = vsb.set
pdb_data.rowconfigure(0, weight=1)

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

Get input values from a grid with several Entry widgets on Tkinter

I'm trying to create a sudoku solver. I have my grid with the entries at this point but I don't know how to get the values that the user has put in them.
I have no clue yet as to how I'm going to make the Sudoku solver but I think I'll first have to find a way to store the input in some variable(s) so I can work with them later on.
So my question is, how do I get the values that have been filled into the entries?
This is my code thus far:
from tkinter import *
root = Tk()
root.title('Sudoku Solver')
root.geometry('500x400')
mylabel = Label(root, text='Fill in the numbers and click solve').grid(row=0, column=0, columnspan=10)
# Create the grid
def beg():
global e
cells = {}
for row in range(1, 10):
for column in range(1, 10):
if ((row in (1,2,3,7,8,9) and column in (4,5,6)) or (row in (4,5,6) and column in (1,2,3,7,8,9))):
kleur='black'
else:
kleur='white'
cell = Frame(root, bg='white', highlightbackground=kleur,
highlightcolor=kleur, highlightthickness=2,
width=50, height=50, padx=3, pady=3, background='black')
cell.grid(row=row, column=column)
cells[(row, column)] = cell
e = Entry(cells[row, column], width=4, bg='white', highlightthickness=0, fg='black', relief=SUNKEN)
e.pack()
# Tell the button what to do
def solve():
global e
test = e.get()
print(test)
# Create the buttons and give them a command
clearer = Button(root, text='Clear', command=beg)
solver = Button(root, text='Solve', command=solve)
# Locate the buttons
clearer.grid(row=11, column=3, pady=30)
solver.grid(row=11, column=7, pady=30)
# Run it for the first time
beg()
root.mainloop()
I also tried changing e to e[row, column] but that gave me a syntax error.
Standard rule: if you have many elements then keep them on list or dictionary.
Do the same as with cells
Create dictionary
entries = {}
add to dictionary
entries[(row, column)] = e
and get from dictionary
def solve():
for row in range(1, 10):
for column in range(1, 10):
print(row, column, entries[(row, column)].get() )
# from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
# --- functions ---
# Create the grid
def beg():
# remove old widgets before creating new ones
for key, val in cells.items():
print(key, val)
val.destroy()
for row in range(1, 10):
for column in range(1, 10):
if ((row in (1,2,3,7,8,9) and column in (4,5,6)) or (row in (4,5,6) and column in (1,2,3,7,8,9))):
kleur='black'
else:
kleur='white'
cell = tk.Frame(root, bg='white', highlightbackground=kleur,
highlightcolor=kleur, highlightthickness=2,
width=50, height=50, padx=3, pady=3, background='black')
cell.grid(row=row, column=column)
cells[(row, column)] = cell
e = tk.Entry(cell, width=4, bg='white', highlightthickness=0, fg='black', relief='sunken')
e.pack()
entries[(row, column)] = e
# Tell the button what to do
def solve():
for row in range(1, 10):
for column in range(1, 10):
print(row, column, entries[(row, column)].get() )
# --- main ---
entries = {}
cells = {}
root = tk.Tk()
root.title('Sudoku Solver')
root.geometry('500x400')
mylabel = tk.Label(root, text='Fill in the numbers and click solve')
mylabel.grid(row=0, column=0, columnspan=10)
# Create the buttons and give them a command
clearer = tk.Button(root, text='Clear', command=beg)
solver = tk.Button(root, text='Solve', command=solve)
# Locate the buttons
clearer.grid(row=11, column=2, pady=30, columnspan=3) # I added `columnspan=3`
solver.grid(row=11, column=6, pady=30, columnspan=3) # I added `columnspan=3`
# Run it for the first time
beg()
root.mainloop()

Categories

Resources