Creating a small add/subtract calculator with tkinter in Python? - python

I've for the most part created the program using the tkinter GUI tools, I just cannot see where I've gone wrong in the actual point where the + or - are clicked. It should be taking the value entered into the entry field, and adding it to the result label, but it's throwing a TypeError saying can only concatenate str (not "StringVar") to str.
import tkinter
class Calculator_GUI:
def __init__(self):
self.mw = tkinter.Tk()
self.mw.title("Calculator")
self.top_frame = tkinter.Frame(self.mw)
self.mid_frame = tkinter.Frame(self.mw)
self.bottom_frame = tkinter.Frame(self.mw)
self.prompt_label = tkinter.Label(self.top_frame, text = "Total: ")
self.num_entry = tkinter.Entry(self.mid_frame, width = 15)
self.add_button = tkinter.Button(self.bottom_frame, text = "+", width = 10, command = self.add)
self.minus_button = tkinter.Button(self.bottom_frame, text = "-", width = 10, command = self.subtract)
self.reset_button = tkinter.Button(self.bottom_frame, text = "Reset", width = 10, command = self.reset)
self.result = tkinter.StringVar()
self.result_label = tkinter.Label(self.top_frame, textvariable = self.result)
self.prompt_label.pack(side = "left")
self.num_entry.pack(side = "left")
self.add_button.pack(side = "left")
self.minus_button.pack(side = "left")
self.reset_button.pack(side = "left")
self.result_label.pack(side = "left")
self.top_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
tkinter.mainloop()
def add(self):
self.result = float(self.num_entry.get() + self.result)
def subtract(self):
self.result = float(self.result - self.num_entry.get())
def reset(self):
self.result.set(0)
calc = Calculator_GUI()

Since self.result is a StringVar, you need to use self.result.set(...) to update its value.
Also you need to convert the strings returned by .get() to number before doing the addition and substraction.
class Calculator_GUI:
def __init__(self):
...
self.num_entry = tkinter.Entry(self.mid_frame, width = 15)
self.num_entry.insert(0, '0') # initialize to zero
...
def add(self):
try:
self.result.set(float(self.num_entry.get()) + float(self.result.get()))
except ValueError as e:
print(e)
def subtract(self):
try:
self.result.set(float(self.result.get()) - float(self.num_entry.get()))
except ValueError as e:
print(e)
...

You need to set and convert correctly
import tkinter
class Calculator_GUI:
def __init__(self):
self.mw = tkinter.Tk()
self.mw.title("Calculator")
self.top_frame = tkinter.Frame(self.mw)
self.mid_frame = tkinter.Frame(self.mw)
self.bottom_frame = tkinter.Frame(self.mw)
self.prompt_label = tkinter.Label(self.top_frame, text = "Total: ")
self.num_entry = tkinter.Entry(self.mid_frame, width = 15)
self.add_button = tkinter.Button(self.bottom_frame, text = "+", width = 10, command = self.add)
self.minus_button = tkinter.Button(self.bottom_frame, text = "-", width = 10, command = self.subtract)
self.reset_button = tkinter.Button(self.bottom_frame, text = "Reset", width = 10, command = self.reset)
self.result = tkinter.StringVar()
self.result.set('0')
self.result_label = tkinter.Label(self.top_frame, textvariable = self.result)
self.prompt_label.pack(side = "left")
self.num_entry.pack(side = "left")
self.add_button.pack(side = "left")
self.minus_button.pack(side = "left")
self.reset_button.pack(side = "left")
self.result_label.pack(side = "left")
self.top_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
tkinter.mainloop()
def add(self):
self.result.set (str(float(self.num_entry.get()) + float(self.result.get())))
def subtract(self):
self.result.set (str(float(self.result.get()) - float(self.num_entry.get())))
def reset(self):
self.result.set('0')
calc = Calculator_GUI()

self.result is of type StringVar and StringVar.get() returns type str, therefore this error makes sense. Moreover, due to the same fact, you need to use the setter method set() in order to set your result label.
I tried it real quick and it works so far with one problem: it concatinates the strings instead of adding their parsed values. You need to parse the str before adding them.

Related

First tkinter project ,trying to find a way to make my tkinter buttons change boolean values for future if statements

I couldnt seem to find a way to make my program realise that ive selected a button, so i changed the function of the celcius to farenheit to try to make it change a boolean value to determine what conversion the program is doing
def celcius_to_farenheit(_event = None):
c_to_f = True
f_to_c = False
the idea being later i can use if statments later in the end result function to find what conversion its doing and display results in the status bar
def end_result():
if c_to_f == True:
converted_temperature = (valid_temperature * 9/5) + 32
label_status.configure(text = converted_temperature, fg = "Orange")
currently i seem to have functions running without me pressing buttons as well, when start the program it immediatly goes to the error message ive created for input muct be numeric even if i havent pressed the celcius to farenheit button.
Any help regarding how to propely have my celcius to farenheit and farenheit to celcius buttons confirm its a float and change a value to use for determining which calculation its using would be helpfull. Knowing why the error message comes up automatically is a bonus.
Below is my code thank you for your time and help.
import sys
from tkinter import *
from tkinter.tix import *
c_to_f = True
def clear_reset(_event = None):
entry_temperature.delete(0, END)
label_status.configure(text = "All data cleared", fg = "Orange")
def end_program(_event = None):
sys.exit()
def convert_temp(_event = None):
try:
valid_temperature = float(entry_temperature.get())
except:
label_status.configure(text = "Input must be numeric", fg = "Orange")
def end_result():
if c_to_f == True:
converted_temperature = (valid_temperature * 9/5) + 32
label_status.configure(text = converted_temperature, fg = "Orange")
def celcius_to_farenheit(_event = None):
c_to_f = True
f_to_c = False
def farenheit_to_celcius(_event = None):
f_to_c = True
c_to_f = False
window = Tk()
window.geometry("550x200")
window.resizable(False, False)
window.title("Temperature Conversion")
tooltip = Balloon(window)
label_input_Temperature = Label(text = "Temperature",fg = "Green")
label_input_Temperature.grid(row= 0, column=0)
entry_temperature = Entry(window, bg = "light blue" )
entry_temperature.grid(row=0, column=1)
temp_button_c_to_f = Button(window, text = "Celcius to Farenheit", command = celcius_to_farenheit)
temp_button_c_to_f.grid(row = 1, column=0)
window.bind('<Shift-c>', celcius_to_farenheit)
tooltip.bind_widget(temp_button_c_to_f, msg = "Shift + C")
temp_button_f_to_c = Button(window, text = "Farenheit to Celcius")
temp_button_f_to_c.grid(row = 1, column = 1 )
conversion_button = Button(window, text = "Convert", command = convert_temp)
conversion_button.grid(row = 2, column = 0,padx =0 )
window.bind('<Enter>', convert_temp)
tooltip.bind_widget(conversion_button, msg = "Enter")
clear_button = Button(window, text = "Clear", command = clear_reset)
clear_button.grid(row = 2, column = 1)
window.bind('<Control-c>', clear_reset)
tooltip.bind_widget(clear_button, msg = "Ctrl + C")
exit_button = Button(window, text = "Exit")
exit_button.grid(row = 2, column = 2, padx = 20, pady = 20)
window.bind('<Control-x>', end_program)
tooltip.bind_widget(exit_button, msg = "Ctrl + X")
label_status = Label(window, width = 50, borderwidth = 2, relief= RIDGE,bg= "Grey" )
label_status.grid(row = 4, column = 1)
tooltip.bind_widget(label_status, msg = "Displays results / error messages")
label_status.configure(text = "Enter in your temperature and select your conversion", fg = "Orange")
window.mainloop()

How can I detect which on which frame was a Button Clicked in Tkinter?

I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()

Giving an error in Boolean statement in python

I am trying to make an app with Python, Tkinter. In this programme in line 47, I have used a boolean statement. But it is giving me an error. I want to use boolean statement as a memory bit and want to use anywhere in programme.
import serial
import Tkinter
from Tkinter import *
class SerialViewer:
def __init__(self):
self.win = Tk()
self.ser = serial.Serial('com8',9600)
def bt1 (self):
self.ser.write('on')
def bt2 (self):
self.ser.write('off')
def bt3 (self):
self.ser.write(self.v.get())
def makewindow (self):
frame1 = Frame(self.win)
frame1.pack(side = LEFT)
b1 = Button(frame1, text = "ON", command = self.bt1)
b2 = Button(frame1, text = "OFF", command = self.bt2)
b1.grid(row = 0, column = 0)
b2.grid(row = 0, column = 1)
frame2 = Frame(self.win)
frame2.pack()
self.v = StringVar()
r1 = Radiobutton(frame2,text = 'on', variable = self.v, value = 'on')
r2 = Radiobutton(frame2,text = 'off', variable = self.v, value = 'off')
r1.select()
b3 = Button(frame2, text = 'send', command = self.bt3)
b3.pack(sid = RIGHT, padx = 5)
r1.pack(side = LEFT)
r2.pack(side = LEFT)
frame3 = Frame(self.win)
frame3.pack()
self.d = StringVar()
self.d.set('default')
label = Label(frame3, textvariable = self.d, relief = RAISED)
label.pack(side = RIGHT)
def update(self):
data = self.ser.readline(self.ser.inWaiting())
self.d.set(data)
if data == 'f1':
self.M1 = True
if self.M1:
print("ok tested")
self.win.after(100,self.update)
def run(self):
self.makewindow()
self.update()
self.win.mainloop()
SerialViewer().run()
The error is:Traceback (most recent call last): File "Untitled",
line 58
SerialViewer().run() File "Untitled", line 55, in run
self.update() File "Untitled", line 49, in update
if self.M1: AttributeError: SerialViewer instance has no attribute 'M1'
I think you should initialize M1 in the constructor. Like this:
def __init__(self):
self.win = Tk()
self.ser = serial.Serial('com8',9600)
self.M1 = False
Because when you get into update method and data != 'f1' it seeks the M1 propriety that doesn't exists.
In your update method, you only define self.M1 when your data is equal to "f1". When it isn't, self.M1 is not defined, so you receive this error.
To fix it, add an else clause to your if statement before, where self.M1 can be set to false.

Python - Tkinter empty window

I'm doing an assignment for a course and I'm not really sure what's up with the code but it runs without error, only displaying an empty window. I used an example given as a start and basically modified it to get this code. If needed I can provide the example code to compare.
from Tkinter import *
class App(Tk):
def __init(self):
Tk.__init__(self)
self.height()
self.weigh()
self.calculate()
self.output()
def height(self):
Label(self, text = "Enter Height, feet").grid()
self.feet = Entry(self)
self.feet.grid(row = 0, column = 1)
self.feet.insert(0, "100")
lblinches = Label(self, text = "Enter Height, inches")
lblinches.grid(row = 1, column = 0)
self.inches = Entry(self)
self.inches.grid(row = 1, column = 1)
def weigh(self):
Label(self, text = "Enter Weight").grid(row =2, column = 0)
self.weight = Entry(self)
self.weight.grid(row = 2, column = 1)
def output(self):
self.calcBMI = Button(self, text = "Calculate BMI")
self.calcBMI.grid()
self.calcBMI["command"] = self.calculate
Label(self, text = "Body Mass Index").grid(row = 4)
Label(self, text = "Status").grid(row = 5)
def calculate(self):
feet1 = int(self.feet.get())
inches1 = int(self.inches.get())
height1 = feet1 *12 + inches1
weight1 = int(self.weight.get())
bmi = (weight1 * 703) / (height1 * 2)
self.lblbmi["text"] = ".2f" % bmi
def main():
app = App()
app.mainloop()
main()
__init should be __init__. Since __init__ was not defined, none of the configuration methods were called.

TKinter process is not drawing Labels, Buttons, and Entry Fields... What am I doing wrong?

I'm a full time student, taking my first class in python.
This is so strange, it was working, and now, it is no longer showing the buttons, labels, and entry fields. I'm sure it was something I removed or added, but, I'm at a loss.
Any ideas? All suggestions are appreciated.
from Tkinter import *
import tkFont
import tkMessageBox
class BouncyGUI(Frame):
"""The GUI used to interface with the bouncy calculation from chapter 9 section 1."""
def __init__(self):
Frame.__init__(self)
# Establish the Base Frame
self.master.title("Calculate the Bounciness of a Ball")
self.master.rowconfigure(0, weight = 1)
self.master.columnconfigure(0, weight = 1)
self.master.grid()
self.master.resizable(0,0)
# Establish the components for capturing the Height
self._heightLabel = Label(self,
text = "Height of initial drop:",
justify = "left")
self._heightLabel.grid(row = 0, column = 0)
self._heightVar = DoubleVar()
self._heightEntry = Entry(self,
textvariable = self._heightVar,
justify = "center")
self._heightEntry.grid(row = 0, column = 1)
# Establish the "bounciness index"
self._bouncyIndex = Label(self,
text = "Bounciness Index:",
justify = "left")
self._bouncyIndex.grid(row = 1, column = 0)
self._bouncyVar = DoubleVar()
self._bouncyEntry = Entry(self,
textvariable = self._bouncyVar,
justify = "center")
self._bouncyEntry.grid(row = 1, column = 1)
self._bouncyVar.set(0.6)
# Establish number of allowable bounces
self._numberBounces = Label(self,
text = "Number of Bounces:",
justify = "left")
self._numberBounces.grid(row = 2, column = 0)
self._numberBouncesVar = IntVar()
self._numberBouncesEntry = Entry(self,
textvariable = self._numberBouncesVar,
justify = "center")
self._numberBouncesEntry.grid(row = 2, column = 1)
# Establish a field for the response
self._answer = Label(self,
text = "Distance Travelled",
justify = "left")
self._answer.grid(row = 3, column = 0)
self._answerVar = DoubleVar()
self._answerFont = tkFont.Font(weight="bold", size = 12)
self._answerEntry = Entry(self,
textvariable = self._answerVar,
justify = "center",
font = self._answerFont)
self._answerEntry.grid(row = 3, column = 1)
self._answerEntry.config(state = DISABLED, bg = "green")
# Create frame to hold buttons
self._buttonFrame = Frame(self)
self._buttonFrame.grid(row = 4, column = 0, columnspan = 2)
# Create Reset Button
self._buttonReset = Button(self._buttonFrame,
text = "Reset",
command = self._reset,
width = 15,
padx = 2,
pady = 2)
self._buttonReset.grid(row = 0, column = 0)
#self._buttonReset.config(state = DISABLED)
# Create Calculate Button
self._buttonCalc = Button(self._buttonFrame,
text = "Calculate",
command = self._calculate,
width = 15,
padx = 2,
pady = 2)
self._buttonCalc.grid(row = 0, column = 1)
#self._buttonCalc.config(state = NORMAL)
def _reset(self):
"""Allow for the screen to reset for fresh data entry."""
self._heightVar.set(0.0)
self._numberBouncesVar.set(0)
self._answerVar.set(0.0)
#self._buttonCalc.config(state = NORMAL)
#self._buttonReset.config(state = DISABLED)
#self._numberBouncesEntry.config(state = NORMAL)
#self._bouncyEntry.config(state = NORMAL)
#self._heightEntry.config(state = NORMAL)
def _calculate(self):
"""Calculate the bounciness and update the GUI"""
if self._validDataTypes():
self._answerVar.set(computeDistance(self._heightVar.get(), \
self._bouncyVar.get(), \
self._numberBouncesVar.get()))
#self._numberBouncesEntry.config(state = DISABLED)
#self._bouncyEntry.config(state = DISABLED)
#self._heightEntry.config(state = DISABLED)
#self._buttonCalc.config(state = DISABLED)
#self._buttonReset.config(state = NORMAL)
def _validDataTypes(self):
theMessage = ""
if self._isInt(self._numberBouncesVar.get()) != True:
theMessage += "Please re-enter Integer Value for Number of Bounces.\n"
elif self._isFloat(self._bouncyVar.get()) != True:
theMessage += "Please re-enter Float Value for Bounciness Index.\n"
elif self._isFloat(self._heightVar.get()) != True:
theMessage += "Please re-enter Float Value for Initial Height."
if len(message) > 0:
tkMessageBox.showerror(message = message, parent = self)
return False
else:
return True
def _isInt(self, value):
# Test to ensure that value entered is an integer
try:
x = int(value)
except ValueError:
# If not return false
return False
# if it is an integer, return true
return True
def _isFloat(self, value):
# Test to ensure that value entered is a float value
try:
x = float(value)
except ValueError:
# If not return false
return False
# If it is a float, return true
return True
def computeDistance(height, index, bounces):
"""Compute the distance travelled."""
total = 0
for x in range(bounces):
total += height
height *= index
total += height
return total
def main():
"""Run the main program"""
BouncyGUI().mainloop()
main()
Your main() function setup code isn't working properly. I'm not sure how you had it set up before, but one way to get it working is this:
def main():
"""Run the main program"""
root = Tk()
gui = BouncyGUI()
gui.pack()
root.mainloop()
You need to grid the main app, not just call its mainloop:
def main()
app = BouncyGUI()
app.grid()
app.mainloop()
there is an error in your code when compiling:
NameError: global name 'message' is not defined

Categories