Is there any way you can make an entry box in tkinter wait for input? (in Python 3)

Viewed 234

I am trying to make a GUI for a calculator based on a tutorial (the link is https://www.youtube.com/watch?v=YXPyB4XeYLA), but I wanted to create the calculator logic by myself.

All of my code is here:

import tkinter as tk

root = tk.Tk()

root.title('Calculator')

ans = tk.Entry(root, borderwidth=4, width=40)
ans.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

def typeNum(number):
    current = ans.get()
    ans.delete(0, tk.END)

    global firstNum

    ans.insert(0, str(current) + str(number))

    firstNum = int(str(current) + str(number))

def eq():
    eqClicked = True

def operation(op):
    opClicked = True
    secNum = 0

    if op == '+':

        ans.delete(0, tk.END)
        ans.wait_var(secNum)
        secNum = int(ans.get())

        eq()
        
        ans.delete(0, tk.END)
        ans.insert(0, firstNum + secNum)

    elif op == '-':
        
        ans.delete(0, tk.END)
        ans.wait_var(secNum)
        secNum = int(ans.get())

        eq()
        
        ans.delete(0, tk.END)
        ans.insert(0, firstNum - secNum)

    elif op == '*':
        
        ans.delete(0, tk.END)
        ans.wait_var(secNum)
        secNum = int(ans.get())

        eq()
        
        ans.delete(0, tk.END)
        ans.insert(0, firstNum * secNum)

    elif op == '/':
        ans.delete(0, tk.END)
        ans.wait_var(secNum)
        secNum = int(ans.get())

        eq()
        
        ans.delete(0, tk.END)
        ans.insert(0, firstNum / secNum)

def clearAll():
    ans.delete(0, tk.END)



button1 = tk.Button(root, text='1', padx=40, pady=20, command=lambda: typeNum(1))
button2 = tk.Button(root, text='2', padx=40, pady=20, command=lambda: typeNum(2))
button3 = tk.Button(root, text='3', padx=40, pady=20, command=lambda: typeNum(3))
button4 = tk.Button(root, text='4', padx=40, pady=20, command=lambda: typeNum(4))
button5 = tk.Button(root, text='5', padx=40, pady=20, command=lambda: typeNum(5))
button6 = tk.Button(root, text='6', padx=40, pady=20, command=lambda: typeNum(6))
button7 = tk.Button(root, text='7', padx=40, pady=20, command=lambda: typeNum(7))
button8 = tk.Button(root, text='8', padx=40, pady=20, command=lambda: typeNum(8))
button9 = tk.Button(root, text='9', padx=40, pady=20, command=lambda: typeNum(9))

button0 = tk.Button(root, text='0', padx=40, pady=20, command=lambda: typeNum(0))
equals = tk.Button(root, text='=', padx=40, pady=20, command=eq)
clear = tk.Button(root, text='CLEAR', padx=23, pady=20, command=clearAll)

add = tk.Button(root, text='+', padx=30, pady=20, command=lambda: operation('+'))
sub = tk.Button(root, text='-', padx=30, pady=20, command=lambda: operation('-'))
mult = tk.Button(root, text='x', padx=30, pady=20, command=lambda: operation('*'))
div = tk.Button(root, text='÷', padx=30, pady=20, command=lambda: operation('/'))

button1.grid(row=1, column=0)
button2.grid(row=1, column=1)
button3.grid(row=1, column=2)

button4.grid(row=2, column=0)
button5.grid(row=2, column=1)
button6.grid(row=2, column=2)

button7.grid(row=3, column=0)
button8.grid(row=3, column=1)
button9.grid(row=3, column=2)

button0.grid(row=4, column=0)
equals.grid(row=4, column=1)
clear.grid(row=4, column=2)

add.grid(row=1, column=3)
sub.grid(row=2, column=3)
mult.grid(row=3, column=3)
div.grid(row=4, column=3)

root.mainloop()

In the 'operation' method, I am having trouble getting my program to wait for the user to enter the second number they would like to use in the calculator.

I keep getting the error of

ValueError: invalid literal for int() with base 10: ''

I thought this was because the program was just taking whatever was in the entry box and trying to convert it into an integer with which it could add to the first number, and it was getting a blank space.

Because of this, I tried to use

time.sleep(20)

so that the program would wait for a little while for me to enter another positive integer, but that just messed up the GUI.

Does anyone know how to get tkinter to wait for an entry? Thanks!

This is my first time using Stack Overflow to as questions, so please excuse any mistakes.

3 Answers

First of all, 'operation' is called a function, not a method. I recommend you to search about what's the difference at Google.

You don't need Tkinter to wait for Entry. You can check it at the function code. For example check if an entry exists if not then return nothing.

For example:

if not ans.get():
    return

^^^ The example checks if an entry exists, if not it'll return None and stop the function from throwing an error. If you want you can read about try, except as well.

You shouldn't be arbitrarily waiting for input using things like sleep, and attempting to read another value inside of your operation function seems like the wrong logical approach. You can avoid needing to wait on input altogether if you take advantage of the event-driven nature of tkinter controls and abstract your code a bit.

This is a very simple solution based on your original program but modified to store operands and operators in an array before evaluating them in order. There may be a few edge cases where this fails if the user inputs an invalid order of operations, but I tried to accommodate for the obvious cases.

The notable difference is this code isn't trying to evaluate anything within the operation routine. It instead stores the operands and operators until = is entered, which is when evaluation takes place.

import tkinter as tk

root = tk.Tk()
root.title('Calculator')
ans = tk.Entry(root, borderwidth=4, width=40)
ans.grid(row=0, column=0, columnspan=4, padx=10, pady=10)

data = []
tmpnum = None

def typeNum(number):
    global tmpnum
    current = ans.get()
    ans.delete(0, tk.END)
    ans.insert(0, str(current) + str(number))
    tmpnum = int(str(current) + str(number))

def eq():
    global tmpnum
    global data
    if tmpnum:
        data.append(tmpnum)
    print("DEBUG")
    print(data)

    # Evaluate
    if len(data) < 3:
        print("ERROR: incomplete equation")
        data.clear()
        return

    firstnum = data.pop(0)
    operator = data.pop(0)
    secondnum = data.pop(0)
    data.clear()

    if operator == "+":
        answer = firstnum + secondnum
    elif operator == "-":
        answer = firstnum - secondnum
    elif operator == "*":
        answer = firstnum * secondnum
    elif operator == "/":
        answer = firstnum / secondnum

    ans.delete(0, tk.END)
    ans.insert(0, str(answer))
    tmpnum = answer # Store result in case you want to use it for next operation

def operation(op):
    global tmpnum
    global data
    ans.delete(0, tk.END)
    if tmpnum:
        data.append(tmpnum)
        tmpnum = None
        data.append(op)

def clearAll():
    global data
    ans.delete(0, tk.END)
    data.clear()



button1 = tk.Button(root, text='1', padx=40, pady=20, command=lambda: typeNum(1))
button2 = tk.Button(root, text='2', padx=40, pady=20, command=lambda: typeNum(2))
button3 = tk.Button(root, text='3', padx=40, pady=20, command=lambda: typeNum(3))
button4 = tk.Button(root, text='4', padx=40, pady=20, command=lambda: typeNum(4))
button5 = tk.Button(root, text='5', padx=40, pady=20, command=lambda: typeNum(5))
button6 = tk.Button(root, text='6', padx=40, pady=20, command=lambda: typeNum(6))
button7 = tk.Button(root, text='7', padx=40, pady=20, command=lambda: typeNum(7))
button8 = tk.Button(root, text='8', padx=40, pady=20, command=lambda: typeNum(8))
button9 = tk.Button(root, text='9', padx=40, pady=20, command=lambda: typeNum(9))

button0 = tk.Button(root, text='0', padx=40, pady=20, command=lambda: typeNum(0))
equals = tk.Button(root, text='=', padx=40, pady=20, command=eq)
clear = tk.Button(root, text='CLEAR', padx=23, pady=20, command=clearAll)

add = tk.Button(root, text='+', padx=30, pady=20, command=lambda: operation('+'))
sub = tk.Button(root, text='-', padx=30, pady=20, command=lambda: operation('-'))
mult = tk.Button(root, text='x', padx=30, pady=20, command=lambda: operation('*'))
div = tk.Button(root, text='÷', padx=30, pady=20, command=lambda: operation('/'))

button1.grid(row=1, column=0)
button2.grid(row=1, column=1)
button3.grid(row=1, column=2)

button4.grid(row=2, column=0)
button5.grid(row=2, column=1)
button6.grid(row=2, column=2)

button7.grid(row=3, column=0)
button8.grid(row=3, column=1)
button9.grid(row=3, column=2)

button0.grid(row=4, column=0)
equals.grid(row=4, column=1)
clear.grid(row=4, column=2)

add.grid(row=1, column=3)
sub.grid(row=2, column=3)
mult.grid(row=3, column=3)
div.grid(row=4, column=3)

root.mainloop()

I think your solution would also be fine if it didn't mess up the GUI. Time.sleep() function completely stops the code including the GUI so I recommend using a threading with tkinter.

https://www.youtube.com/watch?v=FwKQwx91NAM

This video will help your problem with messing up with the GUI.

Related