I am trying to add the math.sqrt function in my py 2.7 calculator. Here is the function I am using:
from Tkinter import *
from math import *
class calculator:
def __init__(self, master):
master.title("Simple Calculator")
self.display = Entry(master)
self.display.grid(columnspan = 8, ipadx = 60, ipady = 10)
Button(master, text = "sqrt", fg = 'white', bg = 'black',
command = lambda:self.sqroot(), height =1, width = 10).grid(row = 6, column = 1)
def sqroot(self):
try:
self.s=self.display.get()
self.sqroot=sqrt(eval(self.s))
self.display.delete(0,END)
self.display.insert(0,self.sqroot)
except:
self.display.delete(0,END)
self.display.insert(0,'Invalid operation')
In the resulting GUI, I can use the 'sqrt' button correctly only once. I am getting the following error message from second time:
TypeError: 'float' object is not callable
calculator.sqroot is, initially, a function (the function that you want).
Then, for the instance of calculator that you're using, you change the function reference self.sqroot to a float reference self.sqroot=sqrt(eval(self.s)). When you try to call that float reference like it's a function, python gets exception-y.
So don't mask names that you've already used and you'll be fine.
You can get it work like this. The problem was because self.sqroot = sqrt(eval(self.s)) changes self.sqroot into a string. To the avoid the problem the code below just stores it in a temporary variable named result. Also note that you could also do something similar with self.s since its value is only needed temporarily inside the method.
Note I also modified your code so it conforms more to the PEP 8 - Style Guide for Python Code guidelines.
from Tkinter import *
from math import *
class calculator:
def __init__(self, master):
master.title("Simple Calculator")
self.display = Entry(master)
self.display.grid(columnspan=8, ipadx=60, ipady=10)
Button(master, text="sqrt", fg='white', bg='black',
command=lambda: self.sqroot(), height=1, width=10).grid(row=6, column=1)
def sqroot(self):
try:
self.s = self.display.get()
# self.sqroot = sqrt(eval(self.s))
result = sqrt(eval(self.s))
self.display.delete(0, END)
# self.display.insert(0, self.sqroot)
self.display.insert(0, result)
except:
self.display.delete(0, END)
self.display.insert(0, 'Invalid operation')
root = Tk()
calculator(root)
root.mainloop()
Related
I have created a class within tkinter that builds a frame and buttons (example below).
I am also trying to create a series of radiobuttons each with a command, and rather than having to type three times the same, I am trying to use a dictionary that stores the function name and the function (again see below):
import tkinter as tk
from tkinter import ttk
from tkinter import *
class EnoxDoseCalculator:
def __init__(self, root):
root.title('Enoxaparin Dose Calculator')
indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
mainframe = ttk.Frame(root)
mainframe.grid(column = 0, row = 0, sticky='nsew')
test_button = ttk.Button(mainframe, text= 'Push Mew!!', command = self.format)
test_button.grid(row = 0, column = 0)
def vte(self):
pass
def af(self):
pass
def mech(self):
pass
def format(self):
self.var3 = tk.StringVar()
for key, value in self.indication.items():
ttk.Radiobutton(mainframe, text = self.indication.key, variable = self.var3, value = self.indication.key, command = self.indication.value).grid(row = n+1, column =0, sticky = 'nswe')
root = Tk()
EnoxDoseCalculator(root)
print(dir(EnoxDoseCalculator))
root.mainloop()
However, I keep getting this message:
I get this is to do with the dictionary functions being listed before they are created, so my question is, where do I place said dictionary, because I am sure this is possible. or is it that I am using the wrong names for the function?
Try this:
class EnoxDoseCalculator:
def __init__(self, root):
self.root = root # add this (mostly for consistency/best practice)
self.root.title('Enoxaparin Dose Calculator')
self.indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
self.mainframe = ttk.Frame(self.root) # update this to include 'self'
self.mainframe.grid(column=0, row=0, sticky='nsew')
# update this to include 'self'
self.test_button = ttk.Button(self.mainframe, text='Push Mew!!', command=self.format)
self.test_button.grid(row=0, column=0)
Using self to namespace your class variables to EnoxDoseCalculator
I am trying to use lambda to create callbacks for tkinter buttons.
There are multiple buttons and each callback needs to pass an object inside it. Following code is what I am doing and is running fine
var0 = tk.StringVar()
label = tk.Label(top, bg = "White",height = 2, width = 12,textvariable=var0, justify="right")
def b0Callback(var):
var.set(var.get()+"0")
return
# creating a label which will print value of the any of the 0-9 button pressed
# creating a button 0
b0 = tk.Button(numFrame0, height = 1, width = 4, bg = "grey", text =
"0",command = lambda: b0Callback(var0))
#there are more buttons like that
var0 is used to update a label. Above code is working fine but I have to create callback for 0 to 9 and I have to just repeat above definition. So I tried using following example from this tutorial
def myfunc(n):
return lambda a : a * n
mydoubler = myfunc(2)
mytripler = myfunc(3)
print(mydoubler(11))
print(mytripler(11))
Using it I did following
def Callback(n):
return lambda var.set(var.get()+n)
b0Callback = Callback("0")
This shows error invalid index in the return line at var.set
Is there any way to pass var0 in this case to avoid this error?
Maybe its only me, but I don't see a reason for using lambda if you just want to add a number to the label text.
Lets make a function for it that gets your StringVar() as a variable and adds some number to it:
def button_callback(str_var, number):
str_var.set(str_var.get() + str(number))
To run this command we simply put it in the code as a lambda function, otherwise it will run upon initialization (because we are providing a function instead of a reference).
So to run it within a button we declare it like this:
my_button = Button(root, text='Some text here', command=lambda: button_callback(my_string_var, 5))
The '5' could be potentially changed to any other number.
I have now solved the problem, here is the final code:
I have also changed the number of buttons to 300 and added code to arrange them all in a nice grid, just for fun. (You can change this to however many you want by changing for number in range(1, whatever).
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.var0 = tk.StringVar()
self.var0.set('0')
# creating buttons and adding them to dictionary
self.buttons = {}
for number in range(1, 301):
self.buttons.update({'button' + str(number): tk.Button(self, height=1, width=4, bg="grey", text=number)})
label = tk.Label(self, textvariable=self.var0, font='none 50')
label.grid(column=0, row=0)
for button in self.buttons:
self.buttons[button].bind('<Button-1>', lambda event, num=button[6:]: self.Callback(event, num))
self.buttons[button].grid(column=(int(button[6:]) % 10), row=(int(button[6:]) / 10) + 1)
def Callback(self, event, num):
self.var0.set(num)
self.update()
I'm writing one of my first programs, and I can't figure out how to fix this issue. I started learning Python like a week ago and am still very new to Tkinter and OOP in general. I got this script (D&D NPC Generator) working without using OOP, but I wanted to see how to do it with OOP since that's how most people seem to prefer using Tkinter.
Here's the code for the Input window:
class Input:
def __init__(self, master):
ideals = ["Good", "Evil",
"Lawful", "Chaotic",
"Neutral", "Other"]
ideal_selection = StringVar()
ideal_selection.set(ideals[0])
self.name_label = Label(master, text="NPC Name: ")
self.name_entry = Entry(master)
self.ideal_label = Label(master, text="Ideal Type: ")
self.ideal_entry = OptionMenu(master, ideal_selection, *ideals)
self.submit_button = Button(text="Submit", command=self.close_window)
self.name_label.grid(row=0, column=0)
self.name_entry.grid(row=0, column=1)
self.ideal_label.grid(row=1, column=0)
self.submit_button.grid(columnspan=2, row=3, column=0)
self.ideal_entry.grid(row=1, column=1)
def close_window(self):
global name
global ideal_type
name = self.name_entry.get()
ideal_type = self.ideal_selection.get()
self.master.destroy()
And it's returning:
AttributeError: 'Input' object has no attribute 'ideal_selection'
I have no idea what's going wrong. My goal with this GUI window is to have the user type in a name for the NPC and then select an option from a drowbown menu for what kind of ideal the user wants the NPC to have. A fix and explanation of what I did wrong would be very helpful, thank you.
You've declared ideal_selection as a local variable and not a class instance variable.
Therefore, calling self.ideal_selection.get() will fail as there is no self to reference.
You need to change the declaration from: ideal_selection = StringVar() to: this.ideal_selection = StringVar() and change all other references over to this.ideal_selection.
Note that you have done this for everything else (self.name_entry) ...
Afterword:
I'd like to discourage you from your use of global here. When you have tkinter running under OOP principles, you can return values from your class back to the calling script.
(Note also eventually I would recommend not using from tkinter import *).
Have a look at what happens if your code is amended to:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import tkinter as tk
import sys
class Input(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
ideals = ["Good", "Evil",
"Lawful", "Chaotic",
"Neutral", "Other"]
self.ideal_selection = tk.StringVar()
self.ideal_selection.set(ideals[0])
self.name_label = tk.Label(root, text="NPC Name: ")
self.name_entry = tk.Entry(root)
self.ideal_label = tk.Label(root, text="Ideal Type: ")
self.ideal_entry = tk.OptionMenu(root, self.ideal_selection, *ideals)
self.submit_button = tk.Button(text="Submit", command=self.close_window)
self.name_label.grid(row=0, column=0)
self.name_entry.grid(row=0, column=1)
self.ideal_label.grid(row=1, column=0)
self.submit_button.grid(columnspan=2, row=3, column=0)
self.ideal_entry.grid(row=1, column=1)
def close_window(self):
#global name
#global ideal_type
self.name = self.name_entry.get()
self.ideal_type = self.ideal_selection.get()
#self.destroy()
self.quit()
if __name__ == '__main__':
root = tk.Tk()
root.geometry("600x400+300+300")
app = Input(root)
root.mainloop()
# Note the returned variables here
# They must be assigned to external variables
# for continued use
returned_name = app.name
returned_ideal = app.ideal_type
print("Your name is: " + returned_name)
print("Your ideal is: " + returned_ideal)
# Should only need root.destroy() to close down tkinter
# But need to handle user cancelling the form instead
try:
root.destroy()
except:
sys.exit(1)
I want to make a GUI calculator. I have used the module tkinter. I made it so that it prints in the terminal but when I try to update it in an Entry it doesn't update. What shoud I do?
from tkinter import *
from tkinter import ttk
import tkinter as tk
class App:
def __init__(self, master):
self.total = DoubleVar(value=0.0)
l_uno = ttk.Label(master, text="Nº 1 = ").grid(row=1, column=1)
l_dos = ttk.Label(master, text="Nº 2 = ").grid(row=1, column=3)
self.uno = ttk.Entry(master)
self.uno.grid(row=1, column=2)
self.dos = ttk.Entry(master)
self.dos.grid(row=1, column=4)
self.bsuma = ttk.Button(master, text="+", command=self.suma).grid(row=3, column=1)
e_resultado = ttk.Entry(master, textvariable=self.total).grid(row=1, column=5)
def suma(self):
data_error = False
try:
resultado = float(self.uno.get()) + float(self.dos.get())
print (resultado)
self.total.set(result)
except:
data_error = True
root = Tk()
alfa = App(root)
root.mainloop()
Your calculation result is being bound to the name resultado, but you are calling self.total.set with the non-existent name result instead of resultado. But your code doesn't report this error because you disabled the error messages with your "naked" except: clause. When catching exceptions it's a Good Idea to use named exceptions, so that you only catch the things you intend to catch, and the except block should use the information that the Exception object provides.
Here's a repaired version of your code.
from tkinter import ttk
import tkinter as tk
class App:
def __init__(self, master):
self.total = tk.DoubleVar(value=0.0)
ttk.Label(master, text="Nº 1 = ").grid(row=1, column=1)
ttk.Label(master, text="Nº 2 = ").grid(row=1, column=3)
self.uno = ttk.Entry(master)
self.uno.grid(row=1, column=2)
self.dos = ttk.Entry(master)
self.dos.grid(row=1, column=4)
ttk.Button(master, text="+", command=self.suma).grid(row=3, column=1)
ttk.Entry(master, textvariable=self.total).grid(row=1, column=5)
def suma(self):
data_error = False
try:
resultado = float(self.uno.get()) + float(self.dos.get())
print (resultado)
self.total.set(resultado)
except ValueError as e:
print(e)
data_error = True
root = tk.Tk()
alfa = App(root)
root.mainloop()
I've also made a few other minor changes to your code. I got rid of the from tkinter import * wildcard import statement. And I got rid of the assignments to the names like l_uno. The .grid method returns None, so those assignments are simply saving None to those names, they aren't saving the widgets. But since you don't need to keep a reference to those widgets there's no need to perform any assignment. When you do need to keep a reference then you need to define the widget on one line and make the .grid call on another line, like you did with self.uno and self.dos.
I am somewhat new to Python, and extremely new to class structures. I am trying to build a calculator GUI with buttons that insert a value into an Entry display. My calculator works without using class structures (the functional code is below), but I'm trying to make the same code work while incorporating the classes. My reason for doing this is to add a "shift" function that changes the values of text on the buttons, and what values (same as the text) they insert into the display. The values as you can see below are class variables, and in order to change the text on the buttons, the shift button is pressed which changes the value of the variables (I have gotten this to work in a non-tkinter file without buttons, etc). The problem is I am getting the GUI with an Entry and two Buttons (good) and '1' in the Entry. (not good) The buttons also don't do anything. Here is my code: (The one that works first)
from tkinter import *
import parser
import math
root = Tk()
root.title('Calculator')
displayb = Entry(root)
displayb.grid(row = 3, columnspan = 6, ipady = 7, ipadx = 100)
def display(e):
global i
i = len(displayb.get())
displayb.insert(i,e)
one = Button(root, text = '1', width = 10, height = 1, command = lambda : display(1))
one.grid(row = 4, column = 0)
root.mainloop()
New Code:
from tkinter import *
import parser
class Calc:
text1 = '1'
shft = 'shft1'
def __init__(self,master):
self.master = master
self.displaya = Entry(master)
self.displaya.grid(row = 1, columnspan = 6)
self.one = Button(master, text = Calc.text1, command = self.display((Calc.text1)))
self.one.grid(row = 4, column = 1)
self.update = Button(master, text = Calc.shft, command = self.update((Calc.shft[4:5])))
self.update.grid(row = 0, column = 0)
def update(self, mode):
if mode == 1:
Calc.text1 = 'A'
Calc.shft = 'shft2'
else:
Calc.text1 = '1'
Calc.shft = 'shft1'
return
def display(self,e):
i = len(self.displaya.get())
self.displaya.insert(i,e)
return
root = Tk()
calc = Calc(root)
root.mainloop()
Can you help me find a solution that makes my code work properly? Thanks
Edit: I attempted using StringVar() and .set, but on the interface, the shift button, instead of saying shft1 or even shft2, said "PY_VAR1". The one button said 1, but inserted "PY_VAR0" to the display. When pressed, the shift button raised this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1699, in __call__
return self.func(*args)
File "/Users/ryanflynn/Classesmod.py", line 13, in <lambda>
self.update_button = Button(master, text = self.shft, command = lambda: self.update(int(self.shft[4:5]))) # use int()
TypeError: 'StringVar' object is not subscriptable
Your code has few errors in it, here is a list of all of them and a modified version of your code:
1st: Your button has the same name as one of your function, change it to a unique name.
2nd: Don't use the class's namespace unless it's necessary, use self instead.
3rd: You only need one set of brackets when calling a function, else it's redundant.
4th: Use lambda or functools.partial to set a command that has arguments.
5th: You can use END(since you did from tkinter import *, otherwise tkinter.END) to specify the ending instead of doing len(self.displaya.get()).
6th: Calc.shft[4:5] returns a string not an int. So in order to get your result, use int(Calc.shft[4:5]).
7th: I didn't see you using parser, so I don't see why you need it.
8th: Don't add a return at the end of a function that returns nothing, it's redundant. Since that's the default for all functions.
This is a modified(working) version of your code:
from tkinter import *
class Calc:
def __init__(self,master):
self.text1 = '1' # make them self's not Calc's
self.shft = 'shft1'
self.master = master
self.displaya = Entry(master)
self.displaya.grid(row = 1, columnspan = 6)
self.one = Button(master, text = self.text1, command = lambda: self.display(self.text1)) # use lambda and single brackets
self.one.grid(row = 4, column = 1)
self.update_button = Button(master, text = self.shft, command = lambda: self.update(int(self.shft[4:5]))) # use int()
self.update_button.grid(row = 0, column = 0)
def update(self, mode):
if mode == 1:
self.text1 = 'A'
self.shft = 'shft2'
else:
self.text1 = '1'
self.shft = 'shft1'
def display(self,e):
self.displaya.insert(END,e) # use END instead
root = Tk()
calc = Calc(root)
root.mainloop()
This code does work but the button text shouldn't change
EDIT:
If you want to use string variable to make the text change, you will need a StringVar as well:
from tkinter import *
class Calc:
def __init__(self,master):
self.master = master
self.displaya = Entry(master)
self.displaya.grid(row = 1, columnspan = 6)
self.text1 = StringVar(value = '1') # change to this
self.shft = StringVar(value = 'shft1')
self.one = Button(master, textvariable = self.text1, command = lambda: self.display(self.text1.get())) # Use .get()
self.one.grid(row = 4, column = 1)
self.update_button = Button(master, textvariable = self.shft, command = lambda: self.update(int(self.shft.get()[4:5])))
self.update_button.grid(row = 0, column = 0)
def update(self, mode):
if mode == 1:
self.text1.set('A') # set the variable
self.shft.set('shft2')
else:
self.text1.set('1')
self.shft.set('shft1')
def display(self,e):
self.displaya.insert(END,e) # use END instead
root = Tk()
calc = Calc(root)
root.mainloop()
When you provide a function to the command argument, you need to provide the actual function, and not the result of calling the function. In other words, you can't have () on the end. One hacky way of handling this is to use lambda as you did earlier:
self.one = Button(master, text = Calc.text1, command = lambda: self.display((Calc.text1)))
A slightly less hacky way is to use functools.partial:
from functools import partial
# ...
self.one = Button(master, text = Calc.text1, command = partial(self.display, Calc.text1))
IMO the best way is to make a real method to use.
self.one = Button(master, text = Calc.text1, command = self.one_pushed)
# ...
def one_pushed(self):
self.display(Calc.text1)
As a side note: tkinter will automatically calculate the end point for you if you pass 'end' as the position:
def display(self,e):
self.displaya.insert('end',e)