Very new to python and Tkinter.
I'm trying to allow a user to enter a number which will then determine the number of lines that are displayed. However when a new number is submitted that is less than the previous number, old row are not deleted.
Thanks in advance!
Example Code:
import Tkinter as tk
from Tkinter import *
root = tk.Tk()
def input_count():
try:
user_submission=int(user_text.get())
except:
wrong_submission=tk.Label(root, text="That isn't a number, try again!", justify = tk.LEFT, padx = 20)
wrong_submission.grid(column=0 , row=1)
else:
for num in range(0,user_submission):
old_row=2
new_row=old_row+(2*num)
extra_new_row= new_row + 1
animal_check=tk.Label(root, text='Enter an animal', justify = tk.LEFT, padx = 20)
animal_check.grid(column=0 , row=new_row)
animal_text = Entry(root, width= 50)
animal_text.grid(column=1, row=new_row)
colour_check=tk.Label(root, text='Enter a colour', justify = tk.LEFT, padx = 20)
colour_check.grid(column=0 , row=extra_new_row)
colour_text = Entry(root, width= 50)
colour_text.grid(column=1, row=extra_new_row)
user_label=tk.Label(root, text='Enter a number', justify = tk.LEFT, padx = 20)
user_label.grid(column=0 , row=0)
user_text= Entry(root, width= 50)
user_text.grid(column=1, row=0)
user_submit=Button(root,text="SUBMIT", command=input_count)
user_submit.grid(column=2,row=0)
root.mainloop()
You can split your root window into two frames, one that contains the user entry, the other one that contains all the new lines. Then you can use the method 'grid_forget' on the second frame to delete it and create a new frame each time the user submit a number of lines.
import os, sys
from Tkinter import *
root = tk.Tk()
def input_count():
try:
user_submission=int(user_text.get())
except:
wrong_submission=tk.Label(frame_top, text="That isn't a number, try again!", justify = tk.LEFT, padx = 20)
wrong_submission.grid(column=0 , row=1)
else:
try:
frame_bottom.grid_forget()
except:
pass
frame_bottom = tk.Frame(root)
frame_bottom.grid(row = 2, column = 0, sticky = "nsew")
for num in range(0,user_submission):
old_row=2
new_row=old_row+(2*num)
extra_new_row= new_row + 1
animal_check=tk.Label(frame_bottom, text='Enter an animal', justify = tk.LEFT, padx = 20)
animal_check.grid(column=0, row=new_row)
animal_text = Entry(frame_bottom, width= 50)
animal_text.grid(column=1, row=new_row)
colour_check=tk.Label(frame_bottom, text='Enter a colour', justify = tk.LEFT, padx = 20)
colour_check.grid(column=0, row=extra_new_row)
colour_text = Entry(frame_bottom, width= 50)
colour_text.grid(column=1, row=extra_new_row)
frame_top = tk.Frame(root)
frame_top.grid(row = 0, column = 0, sticky = "nsew")
frame_bottom = tk.Frame(root)
frame_bottom.grid(row = 2, column = 0, sticky = "nsew")
user_label=tk.Label(frame_top, text='Enter a number', justify = tk.LEFT, padx = 20)
user_label.grid(column=0 , row=0)
user_text= Entry(frame_top, width= 50)
user_text.grid(column=1, row=0)
user_submit=Button(frame_top,text="SUBMIT", command=input_count)
user_submit.grid(column=2,row=0)
root.mainloop()
You need to make a container to keep a reference to your rows. To keep things neat, lets put all the components of a row into a class. Then we can have a destroy() method that destroys all of the row, and a get() method that gets the result from the row. We'll make a global list called "current_rows" that keeps all the rows that are currently displayed. We can add to that list to add rows, and remove from that list to delete rows.
import Tkinter as tk
from tkMessageBox import showerror
class Mgene:
def __init__(self, master):
columns, rows = master.grid_size()
self.animal_check=tk.Label(master, text='Enter an animal', justify = tk.LEFT, padx = 20)
self.animal_check.grid(column=0 , row=rows)
self.animal_text = tk.Entry(master, width= 50)
self.animal_text.grid(column=1, row=rows)
self.colour_check=tk.Label(master, text='Enter a colour', justify = tk.LEFT, padx = 20)
self.colour_check.grid(column=0 , row=rows+1)
self.colour_text = tk.Entry(master, width= 50)
self.colour_text.grid(column=1, row=rows+1)
def destroy(self):
self.animal_check.destroy()
self.animal_text.destroy()
self.colour_check.destroy()
self.colour_text.destroy()
def get(self):
return self.animal_text.get(), self.colour_text.get()
current_rows = []
def input_count():
try:
user_submission=int(user_text.get())
except:
showerror("Error", "That isn't a number, try again!")
else:
# add any rows needed
for num in range(len(current_rows)-1, user_submission):
current_rows.append(Mgene(root))
# remove any rows needed
while len(current_rows) > user_submission:
current_rows.pop().destroy()
def get_answers():
for row in current_rows:
print row.get()
root = tk.Tk()
user_label=tk.Label(root, text='Enter a number', justify = tk.LEFT, padx = 20)
user_label.grid(column=0 , row=0)
user_text= tk.Entry(root, width= 50)
user_text.grid(column=1, row=0)
user_submit=tk.Button(root,text="SUBMIT", command=input_count)
user_submit.grid(column=2,row=0)
user_get=tk.Button(root,text="print answers", command=get_answers)
user_get.grid(column=2,row=1)
root.mainloop()
Note this gives the extra advantage that when the time comes to get the user data out, we have a nice list of rows we can iterate over.
Related
I am relatively new to tkinter, and very new to coding with classes. My GUI will have a step that asks user to enter in an integer with any number of digits to an Entry(). After the last digit keypress (no keypress in 2000 ms) another function will activate that uses the integer entered. It seems very simple but I cannot seem to get this right. I haven't figured out the .after() part, for now I am just using a binding on the ENTER key. Details on the problems, then the code below. I feel like I am struggling way too much on something that can't be that difficult. Please help.
(Some?) Details:
I am getting a bunch of errors leading up to entry input, regarding 'val' not being defined, despite it being defined.
Pressing ENTER does not result in any return value (I added a print command to the mouse clicks just to see if 'val' was being assigned)
Losing sleep from trying to apply after() function
Code (I tried to strip down to essentials best I could, my problem is near the bottom):
'''
import tkinter as tk
import tkinter.messagebox
kk = 1
class App(tk.Tk):
WIDTH = 1000
HEIGHT = 520
def __init__(self):
super().__init__()
self.geometry(f"{App.WIDTH}x{App.HEIGHT}")
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
self.fr0 = tk.Frame(master=self, background = 'grey')
self.fr0.pack(fill = 'both', expand = True, padx = 20, pady = 20)
# configure grid layout (2x1)
self.fr0.columnconfigure(0, weight = 1)
self.fr0.columnconfigure(1, weight = 1)
self.fr0.rowconfigure((1,2,3), weight = 1)
# Title
self.lab_bcwt = tk.Label(self.fr0, text = 'Title', font=("Roboto Medium", 40), justify = 'center')
self.lab_bcwt.grid(row = 0,rowspan = 2, columnspan = 2)
# ===============================
self.fr1 = tk.Frame(self.fr0)#, fg_color=("black","grey"))
self.fr1.grid(row=2, rowspan = 2, columnspan = 2, sticky = 'nsew', padx = 10, pady = 10)
self.fr1.columnconfigure(0, weight = 1)
self.fr1.columnconfigure(1, weight = 1)
self.fr1.rowconfigure((0,1,2,3,4,5), weight = 1)
# ===============================
self.fr2 = tk.Frame(self.fr1)
self.fr2.grid(row=0, columnspan = 2, sticky = 'new', padx = 10, pady = 10)
self.fr2.grid_rowconfigure(0, weight = 1)
self.fr2.grid_columnconfigure(0, weight = 1)
# ===============================
self.lab1 = tk.Label(self.fr2, text = 'This text appears first\n (click to continue)', font=("Ariel", -22), justify = 'center')
self.lab1.grid( row = 0, columnspan = 2, padx = 10, pady = 5)
self.bind("<Button-1>",self.click)
def on_closing(self, event=0):
self.destroy()
def exmp_gps(self):
self.lab1.destroy()
self.lab2 = tk.Label(self.fr2, text = 'Then this text appears second,\n telling you to input an integer', font=('Ariel', -22))
self.lab2.grid(row = 0, column = 0, sticky = 'new', padx = 10, pady = 10)
self.lab3 = tk.Label(self.fr1, text = 'Any Integer', borderwidth = 2, font = ('Ariel', 12))
self.lab3.grid(row = 1, column = 0, sticky = 'ne', padx = 10)
self.entry1 = tk.Entry(self.fr1, text = 'any integer', justify = 'center')
self.entry1.grid(row = 1, column = 1, sticky = 'nw',padx = 10)
self.entry1.configure(font = ('Arial Rounded MT Bold', 13))
def key_pressed(event):
global val
val = int(self.entry1.get())
# print(val)
self.entry1.bind("<KeyRelease>", key_pressed)
# Ideally
self.entry1.bind("<Return>", print(['Function using', val+1]))
def click(self,event):
global kk
if kk == 1:
self.exmp_gps()
if kk > 1:
print(['Function using', val+1])
if __name__ == "__main__":
app = App()
app.mainloop()
'''
There is few problems in code.
bind needs function's name without () but you use print() so it first runs print() (which returns None) and later it binds None to <Return>. You could bind lambda event:print(...) to run print() when you press Enter.
Code could be more readable if you would use normal self.val instead of global val and if normal function in class def key_pressed(self, event): instead of nested function.
Everytime when you click button then it runs clik() and it runs self.exmp_gps() and it creates new Entry. You should increase kk in click() to run self.exmp_gps() only once. And to make it more readable you could use ie. self.visible = False and later set self.visible = True
You think it can be simple to activate function 2000ms after last keypress - but it is not. It would have to use after() and remeber its ID and time when it was created (to delete it if next key will be pressed before 2000ms, and use new after()).
Using Enter is much, much simpler.
Full code with changes.
I added after() but I didn't test it - maybe in some situations it may run function many times.
import tkinter as tk
import tkinter.messagebox
class App(tk.Tk):
WIDTH = 1000
HEIGHT = 520
def __init__(self):
super().__init__()
self.geometry(f"{App.WIDTH}x{App.HEIGHT}")
self.protocol("WM_DELETE_WINDOW", self.on_closing) # call .on_closing() when app gets closed
self.fr0 = tk.Frame(master=self, background='grey')
self.fr0.pack(fill='both', expand=True, padx=20, pady=20)
# configure grid layout (2x1)
self.fr0.columnconfigure(0, weight=1)
self.fr0.columnconfigure(1, weight=1)
self.fr0.rowconfigure((1, 2, 3), weight=1)
# Title
self.lab_bcwt = tk.Label(self.fr0, text='Title', font=("Roboto Medium", 40), justify='center')
self.lab_bcwt.grid(row=0, rowspan=2, columnspan=2)
# ===============================
self.fr1 = tk.Frame(self.fr0)#, fg_color=("black","grey"))
self.fr1.grid(row=2, rowspan=2, columnspan=2, sticky='nsew', padx=10, pady=10)
self.fr1.columnconfigure(0, weight=1)
self.fr1.columnconfigure(1, weight=1)
self.fr1.rowconfigure((0, 1, 2, 3, 4, 5), weight=1)
# ===============================
self.fr2 = tk.Frame(self.fr1)
self.fr2.grid(row=0, columnspan=2, sticky='new', padx=10, pady=10)
self.fr2.grid_rowconfigure(0, weight=1)
self.fr2.grid_columnconfigure(0, weight=1)
# ===============================
self.lab1 = tk.Label(self.fr2, text='This text appears first\n (click to continue)', font=("Ariel", -22), justify='center')
self.lab1.grid(row=0, columnspan=2, padx=10, pady=5)
self.bind("<Button-1>", self.click)
self.val = 0
self.visible = False
self.after_id = None
def on_closing(self, event=0):
self.destroy()
def key_pressed(self, event):
self.val = int(self.entry1.get())
print('[key_pressed]', self.val)
if self.after_id:
print('[key_pressed] cancel after:', self.after_id)
self.after_cancel(self.after_id)
self.after_id = None
self.after_id = self.after(2000, self.process_data)
print('[key_pressed] create after:', self.after_id)
def process_data(self):
print('[process_data]')
def exmp_gps(self):
self.lab1.destroy()
self.lab2 = tk.Label(self.fr2, text='Then this text appears second,\n telling you to input an integer', font=('Ariel', -22))
self.lab2.grid(row=0, column=0, sticky='new', padx=10, pady=10)
self.lab3 = tk.Label(self.fr1, text='Any Integer', borderwidth=2, font=('Ariel', 12))
self.lab3.grid(row=1, column=0, sticky='ne', padx=10)
self.entry1 = tk.Entry(self.fr1, text='any integer', justify='center')
self.entry1.grid(row=1, column=1, sticky='nw', padx=10)
self.entry1.configure(font=('Arial Rounded MT Bold', 13))
self.entry1.bind("<KeyRelease>", self.key_pressed)
self.entry1.bind("<Return>", lambda event:print('[Return] Function using', self.val))
def click(self, event):
if not self.visible:
self.visible = True
self.exmp_gps()
else:
print('[click] Function using', self.val)
if __name__ == "__main__":
app = App()
app.mainloop()
I'm making some program, where I input a bunch of stuff into an entry and it gets printed into a row. I also added a feature where you can delete a row. However, when I delete a row, the window does not shrink. The way I actually made the program was by having 2 frames; the main frame with the buttons and entries, and the output or text frame. When I delete a row, it actually appends the data from a list, deletes the frame and all the widgets and reprints the rows, but with out the row I deleted.
The issue with my code, is that when I delete a row, the rows that weren't deleted start to get smaller and compress and secondly, the bottom of the window doesn't move upwards, leaving a blank white space.
Any help would be appreciated, thanks.
actually appending, labelling and printing the row is in function append_entry() and my delete function is delete_row()
from tkinter import *
global main_window
def quit():
main_window.destroy()
def entry_labels():
leader_label = Label(main_frame, text = 'Customer Name')
leader_label.grid(column=0, row=0)
location_label = Label(main_frame, text = 'Receipt Number')
location_label.grid(column=0, row=1)
numcampers_label = Label(main_frame, text = 'Item Hired')
numcampers_label.grid(column=0, row=2)
weather_label = Label(main_frame, text = 'Number Hired')
weather_label.grid(column=0, row=3)
row_label = Label(main_frame, text= 'Row')
row_label.grid(column=3, row=2)
def button():
print_button = Button(main_frame, text = "Print Details", command = append_entry)
print_button.grid(column=3, row=1)
quit_button = Button(main_frame, text= "Quit", command=quit)
quit_button.grid(column=4, row=0)
delete_row_button = Button(main_frame, text = 'Delete Row', command = delete_row)
delete_row_button.grid(column=4, row=3)
def entry():
global name_entry
name_entry = Entry(main_frame)
name_entry.grid(column=1, row=0)
global receipt_entry
receipt_entry = Entry(main_frame)
receipt_entry.grid(column=1, row=1)
global hired_entry
hired_entry = Entry(main_frame)
hired_entry.grid(column=1, row=2)
global num_hired_entry
num_hired_entry = Entry(main_frame)
num_hired_entry.grid(column=1, row=3)
global delete_row_entry
delete_row_entry = Entry(main_frame)
delete_row_entry.grid(column=4, row=2)
def table_headers():
row_header = Label(main_frame, text='Row', font = 'Arial 10 bold')
row_header.grid(column=0, row=4)
customer_header = Label(main_frame, text='Customer Name', font = 'Arial 10 bold')
customer_header.grid(column=1, row=4)
receipt_header = Label(main_frame, text='Receipt Number', font = 'Arial 10 bold')
receipt_header.grid(column=3, row=4)
item_header = Label(main_frame, text='Item Hired', font = 'Arial 10 bold')
item_header.grid(column=2, row=4)
num_header = Label(main_frame, text='Number Hired', font = 'Arial 10 bold')
num_header.grid(column=4, row=4)
def append_entry():
global second_frame
second_frame = Frame(main_window)
second_frame.grid(column=0, row=6)
leader_error_var.set("")
location_error_var.set("")
numcamper_error_var.set("")
weather_error_var.set("")
global name_count
name_count = 0
global ROWS_ABOVE
ROWS_ABOVE = 6
try:
name_entry_str = str(name_entry.get())
hired_entry_str = str(hired_entry.get())
receipt_entry_int = str(receipt_entry.get())
num_hired_entry_int = str(num_hired_entry.get())
if len(name_entry.get()) != 0:
input_data_col1.append([name_entry_str])
input_data_col2.append([hired_entry_str])
input_data_col3.append([receipt_entry_int])
input_data_col4.append([num_hired_entry_int])
counters['total_entries'] += 1
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
while name_count < counters ['total_entries']:
global name
name = Label(second_frame, text=(input_data_col1[name_count][-1])) ##using -1 selects the latest entry in the list
name.grid(column=1, row=name_count + ROWS_ABOVE, padx=50)
item = Label(second_frame, text=(input_data_col2[name_count][-1]))
item.grid(column=2, row=name_count + ROWS_ABOVE, padx=50)
row = Label(second_frame, text=name_count)
row.grid(column=0, row=name_count + ROWS_ABOVE, padx=60)
receipt = Label(second_frame, text=(input_data_col3[name_count][-1]))
receipt.grid(column=3, row=name_count + ROWS_ABOVE, padx=50)
num = Label(second_frame, text=(input_data_col4[name_count][-1]))
num.grid(column=4, row= name_count + ROWS_ABOVE, padx=50)
name_count += 1
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
except:
leader_error_var.set("Check inputs")
#location_error_var.set("please enter a valid num")
#numcamper_error_var.set("numcamper error test")
weather_error_var.set("")
name_entry.delete(0,END)
receipt_entry.delete(0,END)
hired_entry.delete(0,END)
num_hired_entry.delete(0,END)
def delete_row():
user_del =int(delete_row_entry.get())
counters['total_entries'] -= 1
input_data_col1.pop(user_del)
input_data_col2.pop(user_del)
input_data_col3.pop(user_del)
input_data_col4.pop(user_del)
data = [input_data_col1,input_data_col2,input_data_col3,input_data_col4]
for widget in second_frame.winfo_children():
widget.destroy()
append_entry()
print(input_data_col1)
print(input_data_col2)
print(input_data_col3)
print(input_data_col4)
def error_prevention():
#leader_error_var.set("leader error test")
#location_error_var.set("location error test")
#numcamper_error_var.set("numcamper error test")
#weather_error_var.set("weather error test")
#weather_error_var.set("_______________")
leader_error = Label(main_frame, textvariable = leader_error_var, fg = 'red')
leader_error.grid(column=2, row=0)
location_error = Label(main_frame, textvariable = location_error_var, fg = 'red')
location_error.grid(column=2, row=1)
numcamper_error = Label(main_frame, textvariable = numcamper_error_var, fg = 'red', width = 13)
numcamper_error.grid(column=2, row=2)
weather_error = Label(main_frame, textvariable = weather_error_var, fg = 'red')
weather_error.grid(column=2, row=3)
def main():
global main_window
main_window = Tk()
global input_data_col1
input_data_col1 = []
global input_data_col2
input_data_col2 = []
global input_data_col3
input_data_col3 = []
global input_data_col4
input_data_col4 = []
global input_data
input_data = []
global main_frame
main_frame = Frame(main_window)
main_frame.grid(row=0,column=0)
global counters
counters = {'total_entries':0, 'name_count':0}
#global number
#number = {'total_entries':0}
def stringvars():
global location_error_var
location_error_var = StringVar()
location_error_var.set("")
global numcamper_error_var
numcamper_error_var = StringVar()
numcamper_error_var.set("")
global leader_error_var
leader_error_var = StringVar()
leader_error_var.set("")
global weather_error_var
weather_error_var = StringVar()
leader_error_var.set("")
stringvars()
entry_labels()
entry()
error_prevention()
button()
table_headers()
main()
main_window.mainloop()
Under the code
for widget in second_frame.winfo_children():
widget.destroy()
add this block of code
second_frame.pack()
it will be like this
for widget in second_frame.winfo_children():
widget.destroy()
second_frame.pack()
I hope this helps you
You can use
main_window.geometry("1200x800+100+100")
to change size and position of main_window. Here the window width will be 1200 and height will be 800, positioned 100px to the top left corner of screen. The +100+100 is optional.
You may add geometry() to delete_row callback to resize the frame. Though you might have to calculate the proper size after widget deletion.
As to "when I delete a row, the rows that weren't deleted start to get smaller and compress",
That's due to how grid layout works. If there are two widgets on the same grid row, the grid height will be equal the the higher widget height. When the higher widget is removed, the grid will resize to the smaller widget height and appear to 'shrink'.
To solve that, you can try add padding to the widget, the syntax is
widget.grid(column=1,row=1,padx=(10,10),pady=(10,10))
Alternatively, you may try other layout management: .place will give you absolute layout control. Or you can use pack and let tkinter decide the proper position.
This code is a part of my project in which I have to manage the attendance of 50 (or more) students.
The thing I want is that all the checkboxes 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. Also, if there is another way of creating multiple scrollable checkboxes without packing them inside a canvas then please do tell!
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()
First you need StringVar for each Checkbutton in order to get the state of the Checkbuttons later. Then you can use a list to hold the StringVars so that you can access them later. You can set the onvalue of the checkbuttons to the roll number associated to them.
Also you can use Text widget instead of Canvas+Frame. Below is an example:
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
text = tk.Text(frame, width=40, height=20)
text.pack(side=tk.LEFT, fill=tk.BOTH)
vars = []
for i in range(51):
rollnum = '2018-MC-'+str(i+1)
var = tk.StringVar(value=rollnum)
cb = tk.Checkbutton(text, text=rollnum, variable=var, onvalue=rollnum, offvalue='', bg='white')
text.window_create('end', window=cb)
text.insert('end', '\n')
vars.append(var)
vsb = tk.Scrollbar(frame, orient=tk.VERTICAL, command=text.yview)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
text.config(yscrollcommand=vsb.set)
def submit():
# extract roll numbers for checked checkbuttons
result = [var.get() for var in vars if var.get()]
print(result)
tk.Button(root, text='Submit', command=submit).pack()
root.mainloop()
I'm designing an app in tkinter, but there is a button that won't show up. The button is crucial to the program's operation.
import tkinter as tk
global field
root = tk.Tk()
root.resizable(0,0)
root.geometry('368x200')
header = tk.Label(root, text = 'Header Text', pady=20)
header.config(font = ('Tahoma', 24))
header.grid(row = 0, columnspan=2)
enter_here = tk.Label(root, text = 'Question: ')
enter_here.grid(row = 1, column = 0, pady = 50)
field = tk.Entry(root, width = 50)
field.grid(row = 1, column = 1, pady = 50)
answer = tk.Button(root, text = 'Answer', command = answerf, width=10)
answer.grid(row=2, column=2)
root.mainloop()
Title, header text and the letters are all placeholders. I just need to figure out how to use the button. I've looked around and couldn't find any answers; most people had just forgot a geometry manager.
You have to be careful what values you are passing to various parameters of tkinter widgets. In above case, this is the reason why you are not able to see button.
Change
field = tk.Entry(root, width = 50)
field.grid(row = 1, column = 1, pady = 50)
to
field = tk.Entry(root, width = 25)
field.grid(row = 1, column = 1, pady = 30)
And,
answer = tk.Button(root, text = 'Answer', command = answerf, width=10)
answer.grid(row=2, column=2)
to
answer = tk.Button(root, text = 'Answer', command = answerf, width=10)
answer.grid(row=1, column=2)
output:
The problem is simply that you're forcing the window to be a size that is too small for the objects inside it.
A simple fix is to remove this line:
root.geometry('368x200')
If you insist on keeping this line, then you need to adjust the parameters of the other widgets so that they fit in the constrained space. For example, you could reduce the size of the widgets or reduce the padding.
This should be a very very simple problem. I'm making a GUI in which I have multiple entry widgets... about 30 or so all in one column. Instead of making each box one by one it seems like a better idea to just generate the widgets with a loop. However, I'm finding it extremely difficult to .get() values from the entry widgets, and convert them into floats. This is what I have so far... any help would be greatly appreciated.
class Application(Frame):
def __init__(root,master):
Frame.__init__(root,master)
root.grid()
root.create_widgets()
def calcCR(root):
d1 = root.enter.get()
d1 = float(d1)
#root.answer.delete(0.0,END)
a = 'The C/R Alpha is! %lf \n' % (d1)
root.answer.insert(0.0, a)
def create_widgets(root):
### Generate Element List ###
for i in range(len(elem)):
Label(root, text=elem[i]).grid(row=i+1, column=0)
### Generate entry boxes for element wt% ###
for i in range(len(elem)):
enter = Entry(root, width = 8)
enter.grid(row = i+1,column=1)
enter.insert(0,'0.00')
root.button = Button(root, text = 'Calculate C/R', command = root.calcCR)
root.button.grid(row=11, column=2, sticky = W, padx = 10)
root.answer = Text(root, width = 50, height = 12.5, wrap = WORD)
root.answer.grid(row=1, column=2, rowspan = 10, sticky = W, padx = 10)
root = Tk()
root.title('C/R Calculator')
app = Application(root)
root.mainloop()
Put the Entry instances into a list.
from tkinter import Tk, Frame, Label, Entry, Button
class App(Frame):
def __init__(root, master):
Frame.__init__(root, master)
root.grid()
root.create_widgets()
def get_values(root):
return [float(entry.get()) for entry in root.entries]
def calc_CR(root):
answer = sum(root.get_values()) #Replace with your own calculations
root.answer.config(text=str(answer))
def create_widgets(root):
root.entries = []
for i in range(20):
label = Label(root, text=str(i))
label.grid(row=i, column=0)
entry = Entry(root, width=8)
entry.grid(row=i, column=1)
entry.insert(0, '0.00')
root.entries.append(entry)
root.calc_button = Button(root, text='Calculate C/R', command=root.calc_CR)
root.calc_button.grid(row=20, column=0)
root.answer = Label(root, text='0')
root.answer.grid(row=20, column=1)
def run(root):
root.mainloop()
root = Tk()
root.title('C/R Calculator')
app = App(root)
app.run()