Background: I am a newbie doing projects to get familiar with python.
Problem: I would like to add an "equal to" sign in my calculator I made using tkinter. How can I do this?
My idea: I commented out the attempt I made. Is there anyway to include if statement with an "and" so I can say something like if method is add and equal to then +
Here is my code:
from tkinter import Tk, Label, Button, IntVar, Entry, END, W, E
class Calculator:
def __init__(self, master):
self.total = 0
self.entered_number = 0
self.title_label = Label(master, text="Calculator")
self.total_label_text = IntVar()
self.total_label_text.set(self.total)
self.total_label = Label(master, textvariable=self.total_label_text)
self.label = Label(master, text="Total:")
vcmd = master.register(self.validate)
self.entry = Entry(master, validate="key", validatecommand=(vcmd, "%P"))
self.addition_button = Button(
master, text="Add", command=lambda: self.update("add")
)
self.subtract_button = Button(
master, text="Minus", command=lambda: self.update("minus")
)
self.equalto_button = Button(
master, text="Equals", command=lambda: self.update("equalto")
)
self.reset_button = Button(
master, text="Reset", command=lambda: self.update("reset")
)
self.close_button = Button(master, text="Close", command=master.quit, bg="red")
# LAYOUT
self.label.grid(row=1, column=1, sticky=W)
self.total_label.grid(row=1, column=0, columnspan=2, sticky=E)
self.entry.grid(row=2, column=0, columnspan=2, sticky=W + E)
self.addition_button.grid(row=3, column=0)
self.subtract_button.grid(row=3, column=1)
self.equalto_button.grid(row=3, column=2)
self.reset_button.grid(row=4, column=0, sticky=W + E)
self.close_button.grid(row=4, column=1)
def validate(self, new_text):
if not new_text:
self.entered_number = 0
return True
try:
self.entered_number = int(new_text)
return True
except ValueError:
return False
def update(self, method):
if method == "add":
self.total += self.entered_number
elif method == "minus":
self.total -= self.entered_number
# elif method == "equalto":
# self.total += self.entered_number
else:
self.total = 0
self.total_label_text.set(self.total)
self.entry.delete(0, END)
root = Tk()
my_gui = Calculator(root)
root.mainloop()
In Python, the "equal to" sign is "==", with modifications being made on the first "=" if you want "greater than", "less than", and etc.
Edit: "=" is an assignment whilst "==" is a comparison.
Related
I'm trying to make a mini timer programme, but I have a question: each frame uses the same function called setTimer, so what frame should I reference in the setTimer function? Here is my code:
def createFrames(self):
self.timer1_frame = Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = Button(self.timer1_frame, text="SET TIMER", command=self.setTimer)
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = Button(self.timer2_frame, text="SET TIMER", command=self.setTimer)
self.setTimer2_button.grid(row=0, column=1, padx=30, pady=20)
self.setTimer3_button = Button(self.timer3_frame, text="SET TIMER", command=self.setTimer)
self.setTimer3_button.grid(row=1, column=0, padx=30, pady=20)
self.setTimer4_button = Button(self.timer4_frame, text="SET TIMER", command=self.setTimer)
self.setTimer4_button.grid(row=1, column=1, padx=30, pady=20)
def setTimer(self):
self.hoursLabel = Label(root, text="Hours: ")
self.minutesLabel = Label(root, text="Minutes: ")
self.secondsLabel = Label(root, text="Seconds: ")
As you can see, I'm referencing root in my setTimer function but I don't think it's correct, what would I put there instead so it knows which frame I'm referring to, rather than having to write 4 lots of the same code (is this possible?)
You could create method with argument frame which is used in Label
def setTimer(self, frame):
self.hoursLabel = Label(frame, text="Hours: ")
# ... rest ...
and then you can use lambda in command= to assign function with argument
self.setTimer1_button = tk.Button..., command=lambda:self.setTimer(self.timer1_frame))
And it works.
But it has one problem: in all frames it assigns labels to the same variables self.hoursLabel, etc. so you can't access labels to change text (ie. to update time). You would have to use separated variables but it would need to keep them on list or dictionary and use frame as key.
import tkinter as tk
class Window():
def __init__(self):
# dictionary for labels
self.labels = {}
self.createFrames()
self.createWidgets()
def createFrames(self):
self.timer1_frame = tk.Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = tk.Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = tk.Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = tk.Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = tk.Button(self.timer1_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer1_frame))
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = tk.Button(self.timer2_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer2_frame))
self.setTimer2_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer3_button = tk.Button(self.timer3_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer3_frame))
self.setTimer3_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer4_button = tk.Button(self.timer4_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer4_frame))
self.setTimer4_button.grid(row=0, column=0, padx=30, pady=20)
def setTimer(self, frame):
self.hoursLabel = tk.Label(frame, text="Hours: ")
self.minutesLabel = tk.Label(frame, text="Minutes: ")
self.secondsLabel = tk.Label(frame, text="Seconds: ")
self.hoursLabel.grid(row=1)
self.minutesLabel.grid(row=2)
self.secondsLabel.grid(row=3)
# remember labels in dictionary
self.labels[frame] = [self.hoursLabel, self.minutesLabel, self.secondsLabel]
# start update time
self.updateTimer(frame, 0)
def updateTimer(self, frame, seconds):
secondsLabel = self.labels[frame][2]
secondsLabel['text'] = "Seconds: {}".format(seconds)
seconds += 1
root.after(1000, self.updateTimer, frame, seconds)
root = tk.Tk()
Window()
root.mainloop()
EDIT:
It can be simpler to use Frame to create own widget with one Frame, one Button, all Labels and variables needed in timer. And later use this widget 4 times in main window.
import tkinter as tk
class MyTimer(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hours = 0
self.minutes = 0
self.seconds = 0
self.button = tk.Button(self, text="SET TIMER", command=self.set_timer)
self.button.grid(row=0, column=0, padx=30, pady=20)
self.hours_label = None
self.minutes_label = None
self.seconds_label = None
def set_timer(self):
if self.hours_label is None:
self.hours_label = tk.Label(self, text="Hours: ")
self.minutes_label = tk.Label(self, text="Minutes: ")
self.seconds_label = tk.Label(self, text="Seconds: ")
self.hours_label.grid(row=1)
self.minutes_label.grid(row=2)
self.seconds_label.grid(row=3)
# reset time
self.hours = 0
self.minutes = 0
self.seconds = 0
# start updating time
self.update_timer()
def update_timer(self):
self.hours_label['text'] = "Hours: {}".format(self.hours)
self.minutes_label['text'] = "Minutes: {}".format(self.minutes)
self.seconds_label['text'] = "Seconds: {}".format(self.seconds)
self.seconds += 1
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
# update again after 1000ms (1s)
root.after(1000, self.update_timer)
class Window():
def __init__(self):
self.createFrames()
def createFrames(self):
self.timer1_frame = MyTimer(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = MyTimer(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = MyTimer(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = MyTimer(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
root = tk.Tk()
Window()
root.mainloop()
I need a bit off help..
In the example below I have two optionsMenus, two entries, and some labels.
What I'm trying to do, is to divide my input from the entry by the labels value, choosen from the optionsMenu, and then show the new value in the next column. But I'm a bit stuck now and can't get it to work.
from tkinter import *
class App(Frame):
def __init__(self, root=None):
Frame.__init__(self, root)
self.materialPrice = {'Brick': 70, 'Rockwool': 50, 'Concrete': 20}
materialvariable1 = StringVar(self, root)
materialvariable1.set("Choose material")
materialvariable2 = StringVar(self, root)
materialvariable2.set("Choose materiale")
self.w1 = OptionMenu(root, materialvariable1, *self.materialPrice, command=self.displayPrice).grid(row=2,
column=0,
columnspan=1,
sticky='WE')
self.w2 = OptionMenu(root, materialvariable2, *self.materialPrice, command=self.displayPrice2).grid(row=3,
column=0,
columnspan=1,
sticky='WE')
self.var = IntVar()
self.var.set(float(0.00))
self.var2 = IntVar()
self.var2.set(float(0.00))
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
self.entry2 = Entry(root, textvariable=self.var2).grid(row=3, column=1)
self.priceVarLabel1 = IntVar()
self.priceVarLabel1.set(float(0.00))
self.priceVarLabel2 = IntVar()
self.priceVarLabel2.set(float(0.00))
self.priceVarValue1 = Label(root, textvariable=self.priceVarLabel1, relief='sunken').grid(row=2,
column=2,
columnspan=1,
sticky='WE')
self.priceVarValue2 = Label(root, textvariable=self.priceVarLabel2, relief='sunken').grid(row=3,
column=2,
columnspan=1,
sticky='WE')
self.label1 = Label(root, textvariable=self.displayResult).grid(row=2, column=3)
self.label2 = Label(root, textvariable=self.displayResult2).grid(row=3, column=3)
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value])
def displayPrice2(self, value):
self.priceVarLabel2.set(self.materialPrice[value])
def displayResult(self):
self.label1.set(self.entry1 / self.priceVarValue1)
def displayResult2(self):
self.label1.set(self.entry1 / self.priceVarValue1)
root = Tk()
app = App(root)
root.title("help")
root.mainloop()
Just add the division to your function:
def displayPrice(self, value):
self.priceVarLabel1.set(self.materialPrice[value] / self.var.get())
You may want to change the starting value to 1 so that you don't get a ZeroDivisionError right off the bat.
BTW, initializing a widget and laying it out on the same line is a well known bug source. Always use 2 lines.
# very bad:
self.entry1 = Entry(root, textvariable=self.var).grid(row=2, column=1)
# good:
self.entry1 = Entry(root, textvariable=self.var)
self.entry1.grid(row=2, column=1)
I am trying to get the input of what page number the user wants. They should type in a number, and click the submit button. To test it, I just want to print whatever they typed and then close the window. I've been following: http://effbot.org/tkinterbook/entry.htm as a guide, but I am stumped.
Why does
print(temp)
not print out a number to console?
right now it prints out:
<bound method IntVar.get of <tkinter.IntVar object at 0x000001FBC85353C8>>
I've cleaned up the code a little bit:
import sys
from file import *
from page import *
from view import View
import tkinter as tk
from tkinter import *
class ViewGui:
def __init__(self):
#Included in the class, but unrelated to the question:
self._file = File("yankee.txt", 25)
self.pages = self._file.paginate()
self.initial_list = self.pages[0].readpage(self._file.fo)
self.initial_string = ''.join(self.initial_list)
# Create root
self.root = Tk()
self.root.wm_title("yankee.txt - page 1")
# Create frame for buttons
self.bframe = Frame(self.root)
self.bframe.pack(side=BOTTOM, fill=X)
self.tbutton = tk.Button(self.bframe, text="Top", command=lambda a="top": self.clicks(a)).pack(side=LEFT, expand=1, fill=X)
self.bbutton = tk.Button(self.bframe, text="Bottom", command=lambda a="bottom": self.clicks(a)).pack(side=LEFT, expand=1, fill=X)
self.ubutton = tk.Button(self.bframe, text="Up", command=lambda a="up": self.clicks(a)).pack(side=LEFT, expand=1, fill=X)
self.dbutton = tk.Button(self.bframe, text="Down", command=lambda a="down": self.clicks(a)).pack(side=LEFT, expand=1, fill=X)
self.pbutton = tk.Button(self.bframe, text="Page", command=lambda a="page": self.pageclicks()).pack(side=LEFT, expand=1, fill=X)
self.qbutton = tk.Button(self.bframe, text="Quit", command=quit).pack(side=LEFT, expand=1, fill=X)
# Create and pack Text
self.T = Text(self.root, height=35, width=60, wrap=NONE)
self.T.pack(side=TOP, fill=X)
# Create and pack Scrollbar
self.S = Scrollbar(self.root, orient=HORIZONTAL, command=self.T.xview)
self.S.pack(side=BOTTOM, fill=X)
# Attach Text to Scrollbar
self.T.insert('1.0', self.initial_string)
self.T.config(xscrollcommand=self.S.set, state=DISABLED)
self.S.config(command=self.T.xview)
def pageclicks(self):
print("pageClicks is getting called at least...")
pop = Tk()
pop.wm_title("Page Number")
pop.label = Label(pop, text="Enter a Page Number:", width=35)
pop.label.pack(side=TOP)
pop.entrytext = IntVar()
Entry(pop, textvariable=pop.entrytext).pack()
pop.submitbuttontext = StringVar()
Button(pop, text="Submit", command=lambda a=pop: self.submitted(a)).pack(side=LEFT, pady=5, padx=40)
pop.cancelbuttontext = StringVar()
Button(pop, text="Cancel", command=pop.destroy).pack(side=LEFT, pady=5, padx=40)
def submitted(self, a):
print('submitted is getting called')
temp = (a.entrytext.get)
print(temp)
def clicks(self, a):
print("you clicked clicks with the " + a)
self.root.wm_title(self._file.filename + " - Page " + self._file.buttonHandler(a))
if __name__ == "__main__":
vg = ViewGui()
vg.root.mainloop()
When you create a new window you should not use Tk(), you must use tk.Toplevel()
Must change:
pop = Tk()
to
pop = tk.Toplevel()
You should also use get(), not just get. Must change:
temp = (a.entrytext.get)
to
temp = a.entrytext.get()
Code:
def pageclicks(self):
print("pageClicks is getting called at least...")
pop = tk.Toplevel()
pop.wm_title("Page Number")
pop.label = Label(pop, text="Enter a Page Number:", width=35)
pop.label.pack(side=TOP)
pop.entrytext = IntVar()
Entry(pop, textvariable=pop.entrytext).pack()
pop.submitbuttontext = StringVar()
Button(pop, text="Submit", command=lambda a=pop: self.submitted(a)).pack(side=LEFT, pady=5, padx=40)
pop.cancelbuttontext = StringVar()
Button(pop, text="Cancel", command=pop.destroy).pack(side=LEFT, pady=5, padx=40)
def submitted(self, a):
print('submitted is getting called')
temp = a.entrytext.get()
print(temp)
Made changes to these two methods and now it is working.
def pageclicks(self):
print("pageClicks is getting called at least...")
pop = Tk()
pop.wm_title("Page Number")
pop.label = Label(pop, text="Enter a Page Number:", width=35)
pop.label.pack(side=TOP)
pop._entry = Entry(pop)
pop._entry.pack()
pop._entry.focus_set()
Button(pop, text="Submit", command=lambda a=pop: self.submitted(a)).pack(side=LEFT, pady=5, padx=40)
Button(pop, text="Cancel", command=pop.destroy).pack(side=LEFT, pady=5, padx=40)
def submitted(self, _pop):
temp = _pop._entry.get()
print(temp)
_pop.destroy()
I'm beginner for python and I tried to make a BMI calculator
but i have some problems with the input and output
I want to get input from self.Heighttypeand self.Weighttypeand give a output at self.BMI
And some tips to simply my coding?
from tkinter import *
import tkinter.messagebox
root = Tk()
root.resizable(0,0)
class win1:
def __init__(self, master):
self.master = master
master.title("BMI Calculator")
#
self.he = IntVar()
self.we = IntVar()
self.height = Label(master, text="ENTER Your Height(cm) Here:")
self.Heighttype = Entry(master, textvariable=self.he) #here
self.weight = Label(master,text="ENTER Your Weight(kg) Here:")
self.Weighttype = Entry(master, textvariable=self.we) #and here
#
self.ans = IntVar()
self.BMI = Label(master, textvariable=self.ans) #output here
self.credit = Button(master, text="Credit", command=self.credit_show)
self.result = Button(master, text="Result", command=self.calculation)
root.protocol('WM_DELETE_WINDOW', self.ask_quit)
self.close = Button(master, text="Close", command=self.ask_quit)
self.height.grid(sticky=E, padx=2, pady=4)
self.Heighttype.grid(row=0, column=1, columnspan=2, padx=2)
self.weight.grid(sticky=E, padx=2, pady=4)
self.Weighttype.grid(row=1, column=1, columnspan=2, padx=2)
self.BMI.grid(row=2, column=1, columnspan=2, padx=2)
self.credit.grid(row=3, sticky=W, padx=4 , pady=4)
self.result.grid(row=3, column=1, pady=4, sticky=W+E, padx=4)
self.close.grid(row=3, column=2, pady=4, sticky=W+E, padx=1)
def calculation(self):
# i want to get the user input from top and make calculation
# and make a output self.BMI = Label
def credit_show(self):
tkinter.messagebox.showinfo("Credit","Created by BlackLotus")
def ask_quit(self):
if tkinter.messagebox.askokcancel("Quit", "Do you want to Quit?"):
root.destroy()
apps = win1(root)
root.mainloop()
Someone help me please. Thanks a lot.
Use .get() and .set() on the IntVars to get the parameters and set the result:
def calculation(self):
m = self.we.get()
l = self.he.get()
bmi = # calculate the bmi here
self.ans.set(bmi)
Also, while it seems to work with an IntVar as well, it seems more reasonable to make ans a DoubleVar, i.e.:
self.ans = DoubleVar()
So in my program, whenever I put the grid_remove() function it usually deletes the widget. Whenever I run the program for the first time (that is, not losing any points by guessing wrongly), it deletes most of the widgets. However, whenever I guess wrong and get points taken away (which works fine), the widgets lazily remain on the window. Any help?
Here's the code: (I think that the error occurs somewhere after def numright and def wordguess)
import random
from tkinter import *
from tkinter import messagebox
text_doc = open("test.txt", "r")
text = text_doc.read()
numbOfletters = len(text)
random_letter = random.randint(0,numbOfletters-1)
letter = text[random_letter]
points = 50
username = ""
is_there = 0
class GUIFramework(Frame):
def __init__(self,master=None):
Frame.__init__(self,master)
self.master.title("Window")
self.master.geometry("420x75")
self.grid(padx=10,pady=10)
self.CreateWidgets()
def CreateWidgets(self):
self.lbText = Label(self, text="Would you like to play \"Find the secret letter?\"", font="Verdana")
self.lbText.grid(row=0, column=0, columnspan = 3)
self.button = Button(self, text="Yes, please", command=self.next, background = "green")
self.button.grid(row=1, column=0)
self.btnDisplay = Button(self, text="No, thanks", command = self.window, background = "red")
self.btnDisplay.grid(row=1, column=1)
def window(self):
self.tl = Toplevel(self)
self.master.destroy()
def next(self):
self.lbText.grid_remove()
self.button.grid_remove()
self.btnDisplay.grid_remove()
self.lbText = Label(self, text="You're currently playing \"Find the secret letter\"", font="Verdana")
self.lbText.grid(row=0, column=0, columnspan = 10)
self.username = Label(self, text="Enter your username")
self.username.grid(row=1, column=0)
self.enText = Entry(self)
self.enText.grid(row=1, column=2, columnspan=5)
self.ok = Button(self, text="OK", command = self.wordguess)
self.ok.grid(row=1, column=8)
def wordguess(self):
global username
self.master.geometry("420x90")
username = self.enText.get()
self.username.grid_remove()
self.enText.grid_remove()
self.word = Label(self, text="You have {0} points, enter a word?".format(50))
self.word.grid(row=1, column=0)
self.entry = Entry(self)
self.entry.grid(row=1, column=1, columnspan=4)
self.ok = Button(self, text="OK", command = self.numright)
self.ok.grid(row=1, column=8)
def numright(self):
global points
global is_there
word = self.entry.get()
word = word.lower()
is_there = word.count(letter)
self.lbText.grid_remove()
self.word.grid_remove()
self.entry.grid_remove()
self.enText.grid_remove()
self.ok.grid_remove()
if is_there > 4:
self.master.geometry("200x70")
self.done = Label(self, text = "Congradulations, you have won!")
self.done.grid(row = 0, column = 0)
self.ok = Button(self, text="OK", command = self.window)
self.ok.grid(row=1, column=0)
else:
if (is_there < 5) and (is_there > 0):
points = points - 5
else:
points = points - 10
self.lbText = Label(self, text="You're currently playing \"Find the secret letter\"", font="Verdana")
self.lbText.grid(row=0, column=0, columnspan = 10)
self.numright = Label(self, text="That word has {} secret letters".format(is_there))
self.numright.grid(row=2, column=0)
self.word = Label(self, text="You have {0} points, enter a word?".format(points))
self.word.grid(row=1, column=0)
self.entry = Entry(self)
self.entry.grid(row=1, column=1, columnspan=4)
def scores(self):
output = open("names.txt", "a")
output.write("{0:2} {1} \n".format(str(points), username))
output.close()
output = open("names.txt", "r")
if __name__ == "__main__":
guiFrame = GUIFramework()
guiFrame.mainloop()
Your problem is the lines (in the else clause of the numright method):
self.numright = Label(self, text="That word has {} secret letters".format(is_there))
self.numright.grid(row=2, column=0)
self.numright is a method in your GUIFramework class, which you stomp on here and reassign it to a Label field. Also you probably have to put the self.ok button back onto the page since you grid_removed it above. I added the lines:
self.ok = Button(self, text="OK", command = self.numright)
self.ok.grid(row=1, column=8)
to the end of the numright method to put the OK button back. I then fiddled about by making a different method which just called self.numright which is how I caught the reassignment bug.