Building calculator using and tkinter - python

I want to build a calculator using tkinter, I am just a beginner.
I think the problem is in the '=' button, it just doesn't work and I can't figure out why.for now i just want button from 1 to 9, plus, minus and equals buttons.when i press a number is display it in the entry but the equals button isnt working. for example if i want to do 1+2, it will show 1 and ill press the plus button then 2 and in the entry ill still see 2 instead of 3
from Tkinter import *
import math
class Calc():
def __init__(self):
self.total=0
self.current=""
self.new_num=True
self.op_pending=False
self.op=""
self.eq=False
def num_press(self,num):
self.eq=False
temp=e.get()
temp2=str(num)
if self.new_num:
self.current=temp2
self.new_num=False
else:
if temp2=='.':
if temp2 in temp:
return
self.current=temp+temp2
self.display(self.current)
def calc_total(self):
self.eq=True
self.current=float(self.current)
if self.op_pending==True:
self.do_sum()
else:
self.total=float(e.get())
def display(self,value):
e.delete(0,END)
e.insert(0,value)
def do_sum(self):
if self.op=="add":
self.total+=self.current
if self.op=="minus":
self.total-=self.current
self.new_num=True
self.op_pending=False
self.display(self.total)
def operation(self,op):
self.current=float(self.current)
if self.op_pending:
self.do_sum()
elif not self.eq:
self.total=self.current
self.new_num=True
self.op_pending=True
self.op=op
self.eq=False
def clear(self):
self.total=0
sum=Calc()
root=Tk()
root.geometry("345x270+200+200")
e = Entry(width=20)
e.grid(row=0,column=4)
b1 = Button(text="1",height=4,width=8,command=lambda:sum.num_press(1)).grid(row=0,column=0)
b2 = Button(text="2",height=4,width=8,command=lambda:sum.num_press(2)).grid(row=0,column=1)
# it goes up to b9
equals = Button(text="=",height=4,width=8,command=lambda:sum.calc_total).grid(row=3,column=2)
add = Button(text="+",height=4,width=8,command=lambda: sum.operation("add")).grid(row=3,column=1)
minus = Button(text="-",height=4,width=8,command=lambda: sum.operation("minus")).grid(row=3,column=0)
clear = Button(text="AC",height=4,width=8,command=lambda:sum.clear).grid(row=3,column=3)
root.mainloop()

You can do it as follows. I have shown for only one operator and one number. You can add more to it.
#This is for python 3
import tkinter as tk #Import Tkinter for python2
from tkinter import *
root=tk.Tk()
X=StringVar()
def calculations():
v=e1.get()
val=eval(v)
l=Label(root, text=str(val)).grid()
e1=Entry(root,textvariable=X)
e1.grid()
b1=Button(root, text="1", command=lambda:e1.insert(END,str(1))).grid()
bp=Button(root, text="+", command=lambda:e1.insert(END,str("+"))).grid()
b=Button(root, text="=", command=lambda: calculations()).grid()
root.mainloop()
Hope this helps

Related

How to execute a tkinter in the middle of a Python program

I have a database that includes students' information. In the middle of the program, I need a Tkinter window to be open. How should I open a GUI window in the middle of a Python program?!
The window must include a search box (Entry) by n_number (dictionary value) to show the name (dictionary key related to the entered n_number) of the student and a list of all students. At the same time that I can search by n_number, I need the list of all students, so I can click on each name and see the courses he/she got. I don't know how can I make a clickable list in Tkinter.
...
from tkinter import *
class Student:
def __init__(self):
self.name = ""
self.n_number = None
self.courses = {}
self.students = {}
def add_stu(self, name, n_number):
self.students[name] = n_number
def find_stu(self, n_number):
return list(self.students.keys())[list(self.students.values()).index(n_number)]
def add_course(self, cname, score):
self.courses[cname] = score
...
Just .destroy your root element after you get what you want from user, consider following simple example:
from tkinter import *
user_input = ""
def get_and_destroy():
global user_input
user_input = entry.get()
root.destroy()
print("Before tkinter")
root = Tk()
entry = Entry(root)
entry.pack()
btn = Button(root, text="Go", command=get_and_destroy)
btn.pack()
mainloop()
print("After tkinter")
print("User typed:",user_input)
As you might observe destroy-ing cause breaking from mainloop()
Create a function with the Tkinter and call it when you need the pop up.
To start a Tkinter in the middle of a program, you just declare a Tk() and end it with a .mainloop(). This will stop it from continuing until the window is closed:
from tkinter import *
import time
print('hi')
time.sleep(1)
print('Tkinter starting in:')
time.sleep(1)
print('3')
time.sleep(1)
print('2')
time.sleep(1)
print('1')
root = Tk()
# Put GUI elements in here
root.mainloop()
print('WHY DID YOU LEAVE ME')
As for clickable lists, I would suggest looking into ttk and ttk treeview
To add another window you do Tk()
from tkinter import *
wn = Tk()
wn.title('Window 1')
window = Tk()
window.title('Window2')

Stop function doesn't work using progressbar

I'm writing a program in tkinter using Progressbar. But there is a problem when I added stop function it doesn't work. When I press "stop" button nothing happens, it should stop loading progressbar. I use Python version 3.8. The code below:
from tkinter import *
from tkinter import ttk
import time
root = Tk()
def run():
pb['maximum']=100
for i in range(101):
time.sleep(0.05)
pb['value']=i
pb.update()
def stop():
pb.stop()
runbutt = Button(root,text="Runprogr",command=run)
runbutt.pack()
stopbutt = Button(root,text="Stopbut",command=stop)
stopbutt.pack()
pb = ttk.Progressbar(root,length=300,orient="horizontal")
pb.pack()
root.geometry("300x300")
root.mainloop()
The cause is that pb.stop couldn't stop the function in run.it will also increase by itself.
You could use .after(ms, callback) to add the value(then you no longer need to use time.sleep()).
If you want to stop it,use .after_cancel():
from tkinter import *
from tkinter import ttk
import time
root = Tk()
root.add_value = None
def run():
def add():
if pb['value'] >= 100:
return
pb['value'] += 1
root.add_value = root.after(50, add)
if root.add_value: # to prevent increasing the speed when user pressed "Runprogr" many times.
return
root.add_value = root.after(50, add)
def stop():
if not root.add_value: # to prevent raising Exception when user pressed "Stopbut" button many times.
return
root.after_cancel(root.add_value)
root.add_value = None
runbutt = Button(root, text="Runprogr", command=run)
runbutt.pack()
stopbutt = Button(root, text="Stopbut", command=stop)
stopbutt.pack()
pb = ttk.Progressbar(root, length=300, orient="horizontal")
pb.pack()
root.geometry("300x300")
root.mainloop()

How can I get the option selected by a user from a combobox in toplevel

I'm new to Python and Tkinter and was trying to create an interface to search & plot data. I created a very simple toplevel window to get the values from a combobox that would be selected from users. However, I find the script would only print the first item in the list if comboxlist2.current(0) was set or it would print nothing, no matter which one is selected in the box. I created a sample script to test this. If I click on the "search & create", then the return values can change according to the user selection in comboxlist1, while it would all return "1" no matter what the user selected in comboxlist2. So may I ask where is the issue and how to solve?
Thanks in advance for the potential suggestions or solutions!
import tkinter as tk
from tkinter import ttk
from tkinter import *
def root_print():
reg_in = comboxlist1.get()
print(reg_in) #print the value selected
def on_click():
tl = Toplevel()
comvalue2 = tk.StringVar()
comboxlist2 = ttk.Combobox(tl,textvariable=comvalue2)
comboxlist2["values"] = ("1","2","3")
comboxlist2.grid()
comboxlist2.current(0) #select the first one as default
#mm = comboxlist2.get()
#print(mm) #print directly
go(comboxlist2,tl)
tl.wait_window()
return
def go(comboxlist2,tl):
mm = comboxlist2.get()
Button(tl,text='go', command=lambda:test(mm)).grid()
def test(mm):
print(mm) #do the same thing for the comboxlist2
root = Tk()
root.title('search') #create an interface
root.geometry('+400+200') #size and position
Label(text='region ').grid(row=2,column=0)
comvalue1 = tk.StringVar()
comboxlist1=ttk.Combobox(root,textvariable=comvalue1)
comboxlist1["values"]=("all","africa","asia","australia","canada","europe","mexico","southamerica","usa")
comboxlist1.grid(row=2,column=1)
comboxlist1.current(0)
Button(text='search & create', command=root_print).grid(row=0,column=4)
Button(text='click', command=on_click).grid(row=1, column=4)
loop = mainloop()#go!
Here is the working code, which should take care of your needs. I have removed the imports and some code snippets which are not useful.
import tkinter as tk
from tkinter import ttk
def root_print():
reg_in = comboxlist1.get()
print(reg_in)
def on_click():
tl = tk.Toplevel()
comvalue2 = tk.StringVar()
comboxlist2 = ttk.Combobox(tl,textvariable=comvalue2)
comboxlist2["values"] = ("1","2","3")
comboxlist2.grid()
comboxlist2.current(0) #select the first one as default
tk.Button(tl,text='go', command=lambda: test(comboxlist2.get())).grid()
tl.wait_window()
def test(mm):
print(mm)
root = tk.Tk()
root.title('search') #create an interface
root.geometry('+400+200') #size and position
tk.Label(text='region ').grid(row=2,column=0)
comvalue1 = tk.StringVar()
comboxlist1=ttk.Combobox(root,textvariable=comvalue1)
comboxlist1["values"]=("all","africa","asia","australia","canada","europe","mexico","southamerica","usa")
comboxlist1.grid(row=2,column=1)
comboxlist1.current(0)
tk.Button(text='search & create', command=root_print).grid(row=0,column=4)
tk.Button(text='click', command=on_click).grid(row=1, column=4)
root.mainloop()

Remove buttons for a few seconds in Python/Tkinter?

I'm writing a program where the user will make a selection based on a target image. I'm trying to get the program to remove the selection buttons and wait 2 seconds after updating the target image before the selection choices are re-presented. The code that I have seems to "disable" the clicked button for 2 seconds, but does not remove either button.
from tkinter import *
import random
root = Tk()
root.geometry("500x500")
def click_b(event):
btn_b.pack_forget()
btn_c.pack_forget()
new_a()
root.update()
root.after(2000, show_btns())
def click_c(event):
btn_b.pack_forget()
btn_c.pack_forget()
new_a()
root.update()
root.after(2000, show_btns())
def new_a():
k = random.randrange(1, 3)
if k == 1:
btn_a.configure(image=a1)
elif k == 2:
btn_a.configure(image=a2)
def show_btns():
btn_b.pack(side=LEFT)
btn_c.pack(side=RIGHT)
a1 = PhotoImage(file="A1.gif")
a2 = PhotoImage(file="A2.gif")
orange = PhotoImage(file="orange_btn.gif")
green = PhotoImage(file="yellowgreen_btn.gif")
btn_a = Button(root, image=a1)
btn_a.pack()
btn_b = Button(root, image=orange)
btn_b.bind('<Button-1>', click_b)
btn_b.pack(side=LEFT)
btn_c = Button(root, image=green)
btn_c.bind('<Button-1>', click_c)
btn_c.pack(side=RIGHT)
root.mainloop()
the issues is in your after() methods.
You need to remove the brackets for the show_btns function call or else tkinter will not run this command properly. If you have a function with no arguments you leave off the () portion.
If you do have arguments then you will need to either provide those arguments in the after statement IE after(2000, some_func, arg1, arg2) or use lambda to create a one off function to do the work like after(2000, lambda: some_func(arg1, arg2)). lambda can be more complicated but this is the basic concept.
change:
after(2000, show_btns())
To:
after(2000, show_btns)
As long as your paths to your images work fine the below code should work as intended.
from tkinter import *
import random
root = Tk()
root.geometry("500x500")
def click_b(event):
btn_b.pack_forget()
btn_c.pack_forget()
new_a()
root.update()
root.after(2000, show_btns)
def click_c(event):
btn_b.pack_forget()
btn_c.pack_forget()
new_a()
root.update()
root.after(2000, show_btns)
def new_a():
k = random.randrange(1, 3)
if k == 1:
btn_a.configure(image=a1)
elif k == 2:
btn_a.configure(image=a2)
def show_btns():
btn_b.pack(side=LEFT)
btn_c.pack(side=RIGHT)
a1 = PhotoImage(file="A1.gif")
a2 = PhotoImage(file="A2.gif")
orange = PhotoImage(file="orange_btn.gif")
green = PhotoImage(file="yellowgreen_btn.gif")
btn_a = Button(root, image=a1)
btn_a.pack()
btn_b = Button(root, image=orange)
btn_b.bind('<Button-1>', click_b)
btn_b.pack(side=LEFT)
btn_c = Button(root, image=green)
btn_c.bind('<Button-1>', click_c)
btn_c.pack(side=RIGHT)
root.mainloop()

Allow user to change default text in tkinter entry widget.

I'm writing a python script that requires the user to enter the name of a folder. For most cases, the default will suffice, but I want an entry box to appear that allows the user to over-ride the default. Here's what I have:
from Tkinter import *
import time
def main():
#some stuff
def getFolderName():
master = Tk()
folderName = Entry(master)
folderName.pack()
folderName.insert(END, 'dat' + time.strftime('%m%d%Y'))
folderName.focus_set()
createDirectoryName = folderName.get()
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
return
b = Button(master, text="OK and Close", width=10, command=callback)
b.pack()
mainloop()
return createDirectoryName
getFolderName()
#other stuff happens....
return
if __name__ == '__main__':
main()
I know next to nothing about tkInter and have 2 questions.
Is over-riding the default entry using global createDirectoryName within the callback function the best way to do this?
How can I make the button close the window when you press it.
I've tried
def callback():
global createDirectoryName
createDirectoryName = folderName.get()
master.destroy
but that simply destroys the window upon running the script.
I don't know how experienced are you in Tkinter, but I suggest you use classes.
try:
from tkinter import * #3.x
except:
from Tkinter import * #2.x
class anynamehere(Tk): #you can make the class inherit from Tk directly,
def __init__(self): #__init__ is a special methoed that gets called anytime the class does
Tk.__init__(self) #it has to be called __init__
#further code here e.g.
self.frame = Frame()
self.frame.pack()
self.makeUI()
self.number = 0 # this will work in the class anywhere so you don't need global all the time
def makeUI(self):
#code to make the UI
self.number = 1 # no need for global
#answer to question No.2
Button(frame, command = self.destroy).pack()
anyname = anynamehere() #remember it alredy has Tk
anyname.mainloop()
Also why do you want to override the deafult Entry behavior ?
The solution would be to make another button and bind a command to it like this
self.enteredtext = StringVar()
self.entry = Entry(frame, textvariable = self.enteredtext)
self.entry.pack()
self.button = Button(frame, text = "Submit", command = self.getfolder, #someother options, check tkitner documentation for full list)
self.button.pack()
def getfolder(self): #make the UI in one method, command in other I suggest
text = self.enteredtext.get()
#text now has whats been entered to the entry, do what you need to with it

Categories

Resources