Related
Just picked up tkinter recently
I have a program where when a user click a [...] button, it will display a toplevel window containing a calendar and [OK] button inside it.
When the user click the [OK] button, I want it to change [startDate] variable, and [labelStartDate] label in the main window.
I need the [startDate] variable for my next data process. and [labelStartDate] label is to show user that the date is changed.
How to achieve that?
I tried to use command=lambda or stringvar, but honestly I am kinda lost trying to apply it to my program.
from datetime import date
from textwrap import fill
import tkinter as tk
from tkinter import ttk
from tkinter import Toplevel
from tkinter import font
from tkcalendar import Calendar
from turtle import color, width
# Define the GUI
class App(tk.Tk):
def __init__(self):
super().__init__()
# root window
self.title('Main Window')
self.geometry('620x570')
global startDate #variable that I want to use for later data processing
startDate = date.today().strftime("%Y/%m/%d/")
#DATE MENU FRAME
DateMenuBar = ttk.LabelFrame(self.master, borderwidth = 1, text='Setting')
subFrame2 = tk.Frame(DateMenuBar, borderwidth = 1, relief = tk.FLAT, pady=0, padx=0)
#SUB FRAME 2
labelStart = tk.Label(subFrame2, text='Start',font=('meiryoui', 15))
labelStartDate = tk.Label(subFrame2, text=startDate,font=('meiryoui', 15))
btnOpenCalendar1 = tk.Button(subFrame2, height=1, background='#eeeeee', text='...', font=('meiryoui', 8), command=self.changeStartDate)
labelStart.pack(side = tk.LEFT, ipadx=10)
labelStartDate.pack(side = tk.LEFT, padx=(30,10))
btnOpenCalendar1.pack(side = tk.LEFT)
subFrame2.pack(fill = tk.X,padx=0, pady=10)
DateMenuBar.pack(fill = tk.X,padx=20, ipadx=20, ipady=20)
def changeStartDate(self):
window = Window(self)
window.grab_set()
class Window(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Pick Date")
self.geometry("250x250")
def selectStartDate():
startDate = cal.get_date()
#I got stuck here, trying to figure out how to change the labelStartDate's text
cal = Calendar(self, selectmode = 'day')
cal.pack(padx=20, pady=10)
frame = tk.Frame(self, borderwidth = 1, relief = tk.FLAT, pady=10, padx=20)
btnOK = tk.Button(frame, height=2,width=8, background='#eeeeee', text='OK', font=('meiryoui', 9),command=selectStartDate)
btnCancel = tk.Button(frame, height=2,width=8, background='#eeeeee', text='Cancel', font=('meiryoui', 9))
btnOK.pack(side = tk.RIGHT, padx=(10,0))
btnCancel.pack(side = tk.RIGHT)
frame.pack(fill = tk.X)
if __name__ == "__main__":
app = App()
app.mainloop()
Edit Note:
I added the missing code to my program so that it can be run by others :)
You can first use tkinter.StringVar() and set the label textvariable to the same, inorder to be able to modify the label's text.
self.labelStartDateVar = tk.StringVar() # Initalizing the text variable
self.labelStartDateVar.set(startDateData.start_date) # Setting initial value of the textvariable.
# Added textvariable as labelStartDateVar
self.labelStartDate = tk.Label(subFrame2, textvariable = labelStartDateVar, font = ('meiryoui', 15))
Further, using some knowledge from this post(of Observer Pattern), it is possible to call a function when a change in the startDate is detected. We do so by defining a new class and using a startDateData object as the global object, and to get the value of startDate itself, we simply need to access it's start_date property startDateData.start_date to set it the same property needs to be set like so -:
startDateData.start_date = cal.get_date()
The full code will look something like this -:
class startDate(object):
def __init__(self):
# Setting the default value as in the OP.
self._start_date = date.today().strftime("%Y年 %m月 %d日")
self._observers = []
return
#property
def start_date(self):
return self._start_date
#start_date.setter
def start_date(self, value):
self._start_date = value
for callback in self._observers:
print('announcing change')
callback(self._start_date)
return
def bind_to(self, callback):
print('bound')
self._observers.append(callback)
startDateData = startDate() # global startDateData object.
# Define the GUI
class App(tk.Tk):
def __init__(self):
super().__init__()
# root window
self.title('Main Window')
self.geometry('620x570')
global startDateData #variable that I want to use for later data processing
###
self.labelStartDateVar = tk.StringVar()
self.labelStartDateVar.set(startDateData.start_date)
startDateData.bind_to(self.updateStartDate) # Binding the updateStartDate function to be called whenever value changes.
###
#SUB FRAME 2
self.labelStart = tk.Label(subFrame2, text='開始',font=('meiryoui', 15))
# Added textvariable as labelStartDateVar
self.labelStartDate = tk.Label(subFrame2, textvariable = self.labelStartDateVar, font = ('meiryoui', 15))
self.btnOpenCalendar1 = tk.Button(subFrame2, height=1, background='#eeeeee', text='...', font=('meiryoui', 8), command=self.changeStartDate)
self.labelStart.pack(side = tk.LEFT, ipadx=10)
self.labelStartDate.pack(side = tk.LEFT, padx=(30,10))
self.btnOpenCalendar1.pack(side = tk.LEFT)
subFrame2.pack(fill = tk.X,padx=0, pady=10)
def updateStartDate(self, startDate) :
self.labelStartDateVar.set(startDate)
return
class Window(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Pick Date")
self.geometry("250x250")
# Globally fetch the startDateData object.
global startDateData
def selectStartDate():
# All binded callbacks will be called, when the value is changed here.
startDateData.start_date = cal.get_date()
cal = Calendar(self, selectmode = 'day')
cal.pack(padx=20, pady=10)
frame = tk.Frame(self, borderwidth = 1, relief = tk.FLAT, pady=10, padx=20)
btnOK = tk.Button(frame, height=2,width=8, background='#eeeeee', text='OK', font=('meiryoui', 9),command=selectStartDate)
btnCancel = tk.Button(frame, height=2,width=8, background='#eeeeee', text='Cancel', font=('meiryoui', 9))
btnOK.pack(side = tk.RIGHT, padx=(10,0))
btnCancel.pack(side = tk.RIGHT)
frame.pack(fill = tk.X)
NOTE: As the code provided in the OP, was not adequate enough to be able to test whether this solution works. Further, as the initial code provided seemed to be incomplete, the full code given in the answer at the end may also seem incomplete but still implements all the features present in the code given in the OP.
EDIT: The previous placement of the line startDateData = startDate() was wrong as it was trying to construct an object of a class before it is defined, now the line has been shifted below the class definition of startDate.
EDIT 2: Fixed some of the typos, as mentioned in the comments by #Mario Ariyanto.
I created a graphical user interface that has 3 checkboxes by using the Checkbox class given in tkinter package. I want to retrieve the screen coordinate of each checkbox. I have been trying to use pyautogui.position() to retrieve the coordianates. However, those values seem wrong because when I used those coordinates for the pyautogui engine to click on the box, it did not click. Also it seems that the window contained the checkboxes does not let me to check the boxes, when I was debugging it. What would be other possible ways that I can fix these problems?
from tkinter import *
import SpeechToDictation as Std
import pyautogui
import time
def speech_to_dictation():
speech_inst = Std.SpeechToDictation()
dictation.append(speech_inst.read_audio())
def user_speech_to_dictation():
main_window = Tk()
button = Button(text='Recording')
button.pack()
# have a while true at here so that when the function executes, it quits out of the loop
button.config(command=speech_to_dictation)
main_window.mainloop()
class Test(Frame):
def __init__(self, parent=None, picks=[]):
Frame.__init__(self, parent)
self.vars = []
self.checkboxes = []
Label(self, text='Lab Works').grid(row=0, padx=10, pady=10)
i = 1
for pick in picks:
var = IntVar()
chk = Checkbutton(self, text=pick, variable=var)
chk.grid(row=i, pady=4, padx=10)
self.vars.append(var)
self.checkboxes.append(chk)
i += 1
def state(self):
return map((lambda var: var.get()), self.vars)
def full_screen(window):
width = window.winfo_screenwidth()
height = window.winfo_screenheight()
window.geometry("%dx%d" % (width, height))
def allstates():
print(list(lng.state()))
def make_test(window):
full_screen(window=root)
window.grid(row=1, column=0)
Button(root, text='Quit', command=root.quit).grid(row=10, padx=10, pady=10)
Button(root, text='Peek', command=allstates).grid(row=12, padx=10, pady=10)
if __name__ == '__main__':
# store dictation at dictation[]
dictation = []
user_speech_to_dictation()
is_string_complete = dictation[0]['text'][:8]
if is_string_complete == 'complete':
start_time = time.time() # returns number of seconds passed since epoch
max_loop_time = 1 # 1 seconds
while True:
if (time.time() - start_time) >= max_loop_time:
root = Tk()
lng = Test(root, ['Blood Count', 'Lipid Panel', 'Hemoglobin A1C'])
make_test(window=lng)
root.state('zoomed')
root.update()
x_position, y_position = pyautogui.position()
print(x_position, y_position)
max_loop_time = time.time() - start_time + max_loop_time
# Coordinate of each boxes: they seem wrong
locations = [(53, 158), (84, 228), (36, 302)]
blood_count_string = dictation[0]['text'][9:]
if blood_count_string == 'blood count':
x_coordinate = locations[0][0]
y_coordinate = locations[0][1]
pyautogui.click(x_coordinate, y_coordinate)
allstates()
root.destroy()
# quit the program after the window is destroyed
if max_loop_time > 2:
break
Here is a quick example of how to get the coordinates and click the checkbutton (simply use .winfo_rootx() and .winfo_rooty()):
import tkinter as tk
import pyautogui as pag
def click_btn():
x = check_btn.winfo_rootx()
y = check_btn.winfo_rooty()
pag.click(x, y)
root = tk.Tk()
check_btn = tk.Checkbutton(root, text='This will be clicked in 3 seconds')
check_btn.pack()
root.after(3 * 1000, click_btn)
root.mainloop()
This is to be a desktop application for opening multiple small databases and running queries on them. So far I've written some code for opening forms as necessary. Is this a good way to do it? Also - the code shown opens two copies of each form - what am I doing wrong? It's my first attempt at Python and I'm a rudimentary programmer so simple answers would be of most help please. TIA (Python 3.9.6)
link_1.py
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
#if another function in the class is called, the __init__ function is run at start up
def __init__(self):
print("in link_1 __init__ instruction")
#the call_function function can be called at start up, or not, and will act accordingly
def call_function(self):
print("in call_function")
#the line below is run at start up whether or not another function in the class is called
print("in link_1")
root.withdraw() #hides the blank form at start up
#if __name__ == "__main__":
#the line below shows the link_2 form, whether or not the if __name__==__main__ condition is used as its condition
link_2.ClassLink_2(root).__init__(root)
#link_3.ClassLink_3(root).__init__(root)
#the line below runs call_function on start up to print text
ClassLink_1().call_function()
root.mainloop()
link_2.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
import link_3
root = Tk()
class ClassLink_2:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).__init__(root)
root.withdraw() #hides the blank form at start up
link_3.py
from tkinter import Tk, Button
from tkinter import * #for Toplevel
root = Tk()
class ClassLink_3:
def __init__(self, master):
self.openNewWindow()
def openNewWindow(self):
newWindow = Toplevel(root) #creates a top level widget with the parent root (first parameter)
newWindow.title("Title opened from link_2")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_2").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_3", command= self.do_add)
self.add_button.grid(row=3, column=1)
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).__init__(root) this file has not yet been made
root.withdraw() #hides the blank form at start up
This is a proposed solution, can be expanded as needed. Constructive suggestions for improvement of the structure or code would be appreciated. TIA. I've left in the details in case they are of use to anyone.
link_1
from tkinter import Tk
import link_2
root = Tk()
class ClassLink_1:
def __init__(self):
print("in link_1 __init__ instruction")
root.withdraw() #hides the blank form at start up
link_2.ClassLink_2(root).openNewWindow(0)
root.mainloop()
link_2
from tkinter import Tk, Button
from tkinter import *
import link_3
root = Tk()
class ClassLink_2:
root.withdraw() #hides the blank form at start up
class_var_1 = 0
inst_var_1 = 0
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
newWindow = Toplevel(root)
newWindow.title("Title opened from link_1")
newWindow.geometry("500x500")
label = Label(newWindow, text ="Opened from link_1").grid(row=1, column=1)
self.add_button = Button(newWindow, text="in ClassLink_2", command= self.do_add)
self.add_button.grid(row=3, column=1)
self.add_button = Button(newWindow, text="increment class_var_1", command= self.inc_class_var_1)
self.add_button.grid(row=5, column=1)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
link_3.ClassLink_3(root).openNewWindow(0)
def inc_class_var_1(self):
ClassLink_2.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
link_3
from tkinter import Tk, Button
from tkinter import *
from tkinter.ttk import Combobox
import tkinter.scrolledtext as tkscrolled
# import link_4 <filename of next form>
root = Tk()
class ClassLink_3:
class_var_1 = 0
inst_var_1 = 0
# ------------------------------- START CLASS CONTROLS -----------------------------
root.withdraw()
def __init__(self, incoming_inst_var_1):
pass
def openNewWindow(self, inst_var_1_to_open):
new_window = Toplevel(root)
self.widget_factory(new_window)
self.inst_var_1 = inst_var_1_to_open
def do_add(self):
print("button pressed")
# link_4.ClassLink_4(root).openNewWindow(0) # <filename of next form>
# ---------------------------------- END CLASS CONTROLS -----------------------------
# -------------------------------------- START CALCS --------------------------------------
def inc_class_var_1(self):
ClassLink_3.class_var_1 += 2
self.inst_var_1 += 1
print("self.class_var_1 = " + (str)(self.class_var_1))
print("self.inst_var_1 = " + (str)(self.inst_var_1))
# ---------------------------------------- END CALCS --------------------------------------
# ---------------------------------------- START FACTORY SHOPS-----------------------------------------
def widget_factory(self, new_window):
self.shop_window(new_window)
self.shop_labels(new_window)
self.shop_buttons(new_window)
self.shop_menu(new_window)
self.shop_listbox(new_window)
self.shop_combobox(new_window)
self.shop_radiobuttons(new_window)
self.shop_checkbuttons(new_window)
self.shop_entries(new_window)
self.shop_canvas(new_window)
self.shop_scale(new_window)
self.shop_scrollbars(new_window)
self.shop_textbox(new_window)
self.shop_menu(new_window)
pass
# ------------------------
def shop_window(self, new_window):
new_window.title("Title opened from link_2")
new_window.geometry("800x900")
def shop_labels(self, new_window):
self.label_1 = Label(new_window, text ="Opened from link_2").grid(row=1, column=1)
def shop_buttons(self, new_window):
self.button_1 = Button(new_window, text="in ClassLink_3", command= self.do_add)
self.button_1.grid(row=3, column=1)
self.button_2 = Button(new_window, text="increment class_var_1", command= self.inc_class_var_1)
self.button_2.grid(row=5, column=1)
def shop_listbox(self, new_window):
data=("one", "two", "three", "four")
self.listbox_1 = Listbox(new_window, height=5, selectmode='multiple')
for num in data:
self.listbox_1.insert(END,num)
self.listbox_1.grid(row=7, column=1)
def shop_combobox(self, new_window):
data=("one", "two", "three", "four")
self.combobox_1 = Combobox(new_window, values=data)
self.combobox_1.grid(row=8, column=3)
def shop_radiobuttons(self, new_window):
var_1 = IntVar()
var_1.set(1)
self.rad_btn_1 = Radiobutton(new_window, text="male", variable=var_1, value=1)
self.rad_btn_2 = Radiobutton(new_window, text="female", variable=var_1, value=2)
self.rad_btn_1.grid(row=9, column=1)
self.rad_btn_2.grid(row=9, column=2)
def shop_checkbuttons(self, new_window):
var_1 = IntVar()
var_2 = IntVar()
self.checkbtn_1 = Checkbutton(new_window, text = "Cricket", variable = var_1)
self.checkbtn_2 = Checkbutton(new_window, text = "Tennis", variable = var_2)
self.checkbtn_1.grid(row=10, column=1)
self.checkbtn_2.grid(row=10, column=2)
def shop_entries(self, new_window):
self.entry_1 = Entry(new_window, width = 20)
self.entry_1.insert(0,'Username')
self.entry_1.grid(row= 11, column=2)
self.entry_2 = Entry(new_window, width = 15)
self.entry_2.insert(0,'password')
self.entry_2.grid(row= 12, column=2)
#might want to place on a frame, see example https://coderslegacy.com/python/list-of-tkinter-widgets/
def shop_canvas(self, new_window):
canvas = Canvas(new_window, bg= 'white', width = 260,height = 260) #this is the background for the figure
# left and top of figure is from [x from left, y from top] of canvas right and bottom of figure from [x from left, y from top] of canvas
coordinates = 20, 50, 210, 230
arc = canvas.create_arc(coordinates, start=0, extent=250, fill="blue") #start is from E, extent is in degrees CCW
arc = canvas.create_arc(coordinates, start=250, extent=50, fill="red")
arc = canvas.create_arc(coordinates, start=300, extent=60, fill="yellow")
canvas.grid(row=2, column=1)
def shop_scale(self, new_window):
self.scale_1 = Scale(new_window, from_=0, to=10, orient=VERTICAL)
self.scale_1.grid(row=15, column=2)
self.scale_2 = Scale(new_window, from_=0, to=10, orient = HORIZONTAL)
self.scale_2.grid(row=15, column=3)
def shop_scrollbars(self, new_window):
self.scroll_vert = Scrollbar(new_window)
self.scroll_vert.grid(row=19, column=3)
self.listbox_3 = Listbox(new_window, yscrollcommand = self.scroll_vert.set )
for line in range(1, 100):
self.listbox_3.insert(END, "Number " + str(line))
self.listbox_3.grid(row=19 , column=2)
self.scroll_vert.config(command = self.listbox_3.yview)
def shop_textbox(self, new_window):
#make a frame with parent new_window
self.frame_textbox_1 = Frame(new_window)
self.frame_textbox_1.grid(row=1, column=5)
#make the textbox with parent frame
self.textbox_1 = Text(self.frame_textbox_1)
self.textbox_1.config(wrap=NONE, width=15, height=8) #width, height are characters x rows permitted wrap values should be WORD CHAR, or NONE
#make the X scrollbar with parent frame
self.scroll_text_horiz = Scrollbar(self.frame_textbox_1, orient="horizontal")
self.scroll_text_horiz.config(command = self.textbox_1.xview)
#make the Y scrollbar with parent frame
self.scroll_text_vert = Scrollbar(self.frame_textbox_1, orient="vertical")
self.scroll_text_vert.config(command = self.textbox_1.yview)
#pack the scrollbars before the texbox to allow them to fill the frame width and height
self.scroll_text_horiz.pack(side=BOTTOM, fill=X)
self.scroll_text_vert.pack(side=RIGHT, fill=Y)
self.textbox_1.pack(fill="y")
#connect the scrollbars to the textbox
self.textbox_1["xscrollcommand"] = self.scroll_text_horiz.set
self.textbox_1["yscrollcommand"] = self.scroll_text_vert.set
message = "the quick brown fox"
self.textbox_1.insert(1.1, message)
# ----------------------------------------------
def shop_menu(self, new_window):
print("in shop menu1")
menubar = Menu(new_window)
print("in shop_menu2")
file = Menu(menubar, tearoff=0)
file.add_command(label="New")
file.add_command(label="Open")
file.add_command(label="Save")
file.add_command(label="Save as...")
file.add_command(label="Close")
file.add_separator()
file.add_command(label="Exit", command=new_window.quit)
menubar.add_cascade(label="File", menu=file)
edit = Menu(menubar, tearoff=0)
edit.add_command(label="Undo")
edit.add_separator()
edit.add_command(label="Cut")
edit.add_command(label="Copy")
edit.add_command(label="Paste")
edit.add_command(label="Delete")
edit.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=edit)
help = Menu(menubar, tearoff=0)
help.add_command(label="About")
menubar.add_cascade(label="Help", menu=help)
new_window.config(menu=menubar)
print("in shop menu3")
# --------------------------------------- END FACTORY SHOPS---------------------------------------
I'm coding a timer app, which has encountered a lot of difficulties, but one of the first ones has been in making a loop that can be broken by the press of a button. I've looked into it, and my research shows I should use threading, but I couldn't figure out how it would work.
What I decided to try, then, was to make an exception when I invoked a keyboard interrupt, and then make a button that calls that same interrupt. However, my current code refuses to interrupt when I ctrl-c.
My sample code looks like this
from Tkinter import *
from sys import exit
class Timer:
def __init__(self, master):
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command= quit)
buttonquit.grid(row = 1, column = 2)
global timertext
timertext = DoubleVar()
timertext.set(0)
display = Label(master, textvariable = timertext)
display.grid(row = 0, column = 0)
timertext.set(timertext)
def timerlogic(self):
pass
def pause(self):
pass
def start(self):
global timertext
try:
while True:
#do things
except KeyboardInterrupt:
print "Interrupted"
def lap(self):
pass
root = Tk()
app = Timer(root)
root.mainloop()
root.destroy()
Basically, I don't think my code as it stands is viable, but I don't know how to edit it to make a loop I can interrupt as needed.
You set some variable to True or False. Also, a while loop interrupts the Tkinter loop so Tkinter will do nothing until the while loop exits. Use Tkinter's after function instead.
from Tkinter import *
##from sys import exit
class Timer:
def __init__(self, master):
self.master=master
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command=self.quitit)
buttonquit.grid(row = 1, column = 2)
self.timertext = DoubleVar()
self.timertext.set(0)
display = Label(master, textvariable = self.timertext)
display.grid(row = 0, column = 0)
## timertext.set(timertext) ## Huh!!
self.timeit=False
def increment_timer(self):
ctr=int(self.timertext.get())
self.timertext.set(ctr+1)
if self.timeit:
self.master.after(500, self.increment_timer)
def start(self):
self.timeit=True
self.increment_timer()
def quitit(self):
self.timeit=False
root = Tk()
app = Timer(root)
root.mainloop()
I have a list of radiobuttons and linked to the same variable and I have a submit button that when clicked I want it to pass the current variable value to another function which will then use that number. I used lambda function but when the function should be called I get global name 'num' is not defined. num is my function. Below is my code. Thank you very much.
from tkinter import *
import random
class App:
def __init__(self, master):
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command= lambda : num (v)
).grid(row = 12, column = 0)
def num( number):
print(number)
root = Tk()
app = App(root)
root.mainloop()
Indent your code correctly.
from tkinter import *
import random
class App:
def __init__(self, master):
self.say_one()
def say_one(self):
v = IntVar()
window = Toplevel(root)
for i in range(1,11):
self.label = Radiobutton(
window, text = i , fg = "red",
value = i, variable = v
).grid(row =i, column =0)
# error is occurring in the next line
self.report = Button(
window, text="submit", command=lambda: num(v)
).grid(row = 12, column = 0)
def num(var):
print(var.get())
root = Tk()
app = App(root)
root.mainloop()
I fixed the indentation, changed the num function to print variable's value instead of printing variable itself.