Problem with dynamically changing which command a button calls in tkinter - python-3.x

In this simple calculator GUI, I'm creating a frame template using classes. The frame has 2 labels, 2 entry boxes, and a button. I'd like the button to run a specific command depending on the function_call variable passed when initializing - but this doesn't work. The two_points function should be called for the first object, and one_point should be called for the second. How do I dynamically change which command is called based on which object I'm using? Thank you for taking the time to read this.
from tkinter import *
root = Tk()
root.title("Simple Slope Calculator")
class Slope_Calc:
# Variable info that changes within the frame
def __init__(self, master, num_1, num_2, frame_name, label_1_name, label_2_name, function_call):
self.num_1 = int(num_1)
self.num_2 = int(num_2)
self.frame_name = frame_name
self.label_1_name = label_1_name
self.label_2_name = label_2_name
self.function_call = function_call
# Frame template
self.frame_1 = LabelFrame(master, text = self.frame_name, padx = 5, pady = 5)
self.frame_1.grid(row = self.num_1, column = self.num_2, padx = 10, pady = 10)
self.label_1 = Label(self.frame_1, text = self.label_1_name)
self.label_1.grid(row = 0, column = 0)
self.entry_1 = Entry(self.frame_1)
self.entry_1.grid(row = 0, column = 1)
self.label_2 = Label(self.frame_1, text = self.label_2_name)
self.label_2.grid(row = 1, column = 0)
self.entry_2 = Entry(self.frame_1)
self.entry_2.grid(row = 1, column = 1)
self.calc_button = Button(self.frame_1, text = "Calculate", command = self.function_call) # This is what doesn't work
self.calc_button.grid(row = 1, column = 2, padx = 5)
# Strips string of spaces and parentheses
# Returns a list of relevant ordered pair
def strip_string(self, entry_num):
ordered_pair = entry_num.get().split(", ")
ordered_pair[0] = ordered_pair[0].replace("(", "")
ordered_pair[1] = ordered_pair[1].replace(")", "")
return(ordered_pair)
# Calculates slope based on one point and y-intercept
def one_point(self):
pair_1 = self.strip_string(self.entry_1)
b = int(self.entry_2.get())
m = (int(pair_1[1]) - b)/(float(pair_1[1]))
label_3 = Label(self.frame_1, text = "SLOPE-INTERCEPT EQUATION: y = " + str(m) + "x + " + str(b))
label_3.grid(row = 2, column = 0, columnspan = 2)
# Calculates slope based on two points given
def two_points(self):
pair_1 = self.strip_string(self.entry_1)
pair_2 = self.strip_string(self.entry_2)
m = (int(pair_2[1]) - int(pair_1[1]))/float(int(pair_2[0]) - int(pair_1[0]))
b = (int(pair_1[1])) - (m*int(pair_1[0]))
label_3 = Label(self.frame_1, text = "SLOPE-INTERCEPT EQUATION: y = " + str(m) + "x + " + str(b))
label_3.grid(row = 2, column = 0, columnspan = 2)
# Calling each object
two_p = Slope_Calc(root, 0, 0, "Two Points", "First Ordered Pair", "Second Ordered Pair", "two_points")
one_p = Slope_Calc(root, 0, 1, "One Point and Y-Intercept", "Ordered Pair", "Y-intercept", "one_point")
root.mainloop()

The command keyword argument of the Button constructor is supposed to be a function.
Here you give it instead a string which is the name of the method of self that should be called. So you must first get this method using setattr to be able to call it. This should do it:
def call():
method = getattr(self, self.function_call)
method()
self.calc_button = Button(
self.frame_1,
text = "Calculate",
command = call)
You then have an error in strip_string but that's another story.

Related

Run function in form (ctd)

Here's my current code and the changes I've made so far from the previous version
I've reset the variables at the start because I did not want them to be reset each time the function was executed
I've added a ess_e variable for the entry of the procedure, I want this variable to store the localtime when the function is executed
I've added a ess_s variable for the exit of the procedure after all the infos are entered which should be localtime when the submit button (changed text) is pushed
The remaining problems are
root.bind does not work, I've tried several alternatives with fenetre as you suggested but I could not make it work.
I get an error when I run the 2nd function (essenceresult)
File "D:\Bureautique\Python\Scripts\interface-test.py", line 57, in essenceResult
ess_e = ent0.get()
AttributeError: 'datetime.datetime' object has no attribute 'get'
I still have to make the frame to display the previous results but I'll get there in due time :)
Thanks a lot for the help
from tkinter import *
from datetime import *
import time
ess=[[],[],[],[],[]]
rel=[[],[],[],[],[]]
c60=[[],[]]
def c60duree(delta):
(h, r) = divmod(delta.seconds, 3600)
(m, s) = divmod(r, 60)
return "%s%02d:%02d:%02d" % (
"%d jour(s) " % delta.days if delta.days > 0 else "",
h,
m,
s,
)
def code60():
saisie=0
while saisie != "X":
c60d=datetime.now()
print ("Code 60 activé à ", c60d.strftime("%H:%M:%S"))
saisie=input("Tapez entree pour la fin du code 60, X pour sortir : ")
c60f=datetime.now()
print("Debut ", c60d.strftime("%H:%M:%S"))
print("Fin ", c60f.strftime("%H:%M:%S"))
c60x=c60f-c60d
print("Duree ", c60duree(c60x))
print("-------------")
c60[0].append(c60d)
c60[1].append(c60f)
#del(c60[0],[-1])
#del(c60[1],[-1])
#return
def relais():
print("Relais pilote demarré ")
relh=datetime.now()
relv=input("Quelle voiture ? ")
relp=input("Quel pilote repart ? ")
rele=input("Quantité d'essence ? ")
input("Tapez entrée à la sortie des stands ")
rels=datetime.now()
rel[0].append(relh), rel[1].append(relv), rel[2].append(relp), rel[3].append(rele), rel[4].append(rels)
print("Dureé ", rels-relh)
print(*rel)
#def essence():
#ess=[[],[],[],[]]
#print("Ravitaillement essence demarré ")
#essh=datetime.now()
#essv=input("Quelle voiture ? ")
#essp=input("Quel pilote ? ")
#essq=input("Combien de litres ? ")
#ess[0].append(essh), ess[1].append(essv), ess[2].append(essp), ess[3].append(essq)
#print(*ess)
def essenceResult(ess, ent0, ent1, ent2, ent3):
ess_e = ent0.get()
essv = ent1.get()
essp = ent2.get()
essq = ent3.get()
ess_s = datetime.now()
ess[0].append(ess_e), ess[1].append(essv), ess[2].append(essp), ess[3].append(essq), ess [4].append(ess_s)
resultLabel = Label(Frame4, text = str(*ess))
resultLabel.grid(row = 5, column = 0, columnspan = 2)
def essence():
print("yr")
ess_e=datetime.now()
Frame4.grid(row = 1, column = 0)
Label1 = Label(Frame4, text = "Ravitaillement demarré ")
Label1.grid(row = 0, column = 0, columnspan = 2)
essvLabel = Label(Frame4, text = "Quelle voiture ? ")
essvEntry = Entry(Frame4)
essvLabel.grid(row = 1, column = 0)
essvEntry.grid(row = 1, column = 1)
essvEntry.focus()
#root.bind_all("<Return>", function)
esspLabel = Label(Frame4, text = "Quel pilote part ? ")
esspEntry = Entry(Frame4)
esspLabel.grid(row = 2, column = 0)
esspEntry.grid(row = 2, column = 1)
esspEntry.focus()
essqLabel = Label(Frame4, text = "Combien de litres ? ")
essqEntry = Entry(Frame4)
essqLabel.grid(row = 3, column = 0)
essqEntry.grid(row = 3, column = 1)
essqEntry.focus()
submitButton = Button(Frame4, text = "Sortie des stands", command = lambda: essenceResult(ess, ess_e, essvEntry, esspEntry, essqEntry))
submitButton.grid(row = 4, column = 0, columnspan = 2)
fenetre = Tk()
fenetre['bg']='grey'
# frame 1
Frame1 = Frame(fenetre, bg="green", borderwidth=1, relief=GROOVE)
Frame1.grid(row = 0, column = 0, padx=5, pady=5)
# frame 2
Frame2 = Frame(fenetre, bg="yellow", borderwidth=1, relief=GROOVE)
Frame2.grid(row = 0, column = 1, padx=5, pady=5)
# frame 3
Frame3 = Frame(fenetre, bg="purple", borderwidth=1, relief=GROOVE)
Frame3.grid(row = 0, column = 2, padx=5, pady=5)
Frame4 = Frame(fenetre)
Fenetre = Tk()
fenetre['bg']='grey'
# Ajout de labels
Button(Frame1, text="Essence", command=essence).pack(padx=300, pady=100)
Button(Frame2, text="Relais", command=relais).pack(padx=300, pady=100)
Button(Frame3, text="Code 60", command=code60).pack(padx=300, pady=100)
Here's the fixed code:
def essenceResult(ess, ess_e, ent1, ent2, ent3):
essv = ent1.get()
essp = ent2.get()
essq = ent3.get()
ess_s = datetime.now()
ess[0].append(ess_e), ess[1].append(essv), ess[2].append(essp), ess[3].append(essq), ess[4].append(ess_s)
resultLabel = Label(Frame4, text = str(ess))
resultLabel.grid(row = 5, column = 0, columnspan = 2)
def essence():
print("yr")
ess_e=datetime.now()
Frame4.grid(row = 1, column = 0)
Label1 = Label(Frame4, text = "Ravitaillement demarré ")
Label1.grid(row = 0, column = 0, columnspan = 2)
essvLabel = Label(Frame4, text = "Quelle voiture ? ")
essvEntry = Entry(Frame4)
essvLabel.grid(row = 1, column = 0)
essvEntry.grid(row = 1, column = 1)
essvEntry.focus()
esspLabel = Label(Frame4, text = "Quel pilote part ? ")
esspEntry = Entry(Frame4)
esspLabel.grid(row = 2, column = 0)
esspEntry.grid(row = 2, column = 1)
esspEntry.focus()
essqLabel = Label(Frame4, text = "Combien de litres ? ")
essqEntry = Entry(Frame4)
essqLabel.grid(row = 3, column = 0)
essqEntry.grid(row = 3, column = 1)
essqEntry.focus()
submitCommand = lambda event = None: essenceResult(ess, ess_e, essvEntry, esspEntry, essqEntry)
submitButton = Button(Frame4, text = "Sortie des stands", command = submitCommand)
Frame4.bind_all("<Return>", submitCommand)
submitButton.grid(row = 4, column = 0, columnspan = 2)
You also need to remove the extra Fenetre = Tk() after Frame4, as this will break it. (You can only have one Tk(), if you want more windows use Toplevel())The first problem is solved by adding Frame4.bind_all("<Return>", submitCommand). Now when you press enter it will run submitCommand, which will run essenceResult.
The second problem was using .get() on a datetime.datetime object. You can't use .get() because it isn't an entry. Instead, just have it as a parameter and don't change it. You only need .get() to get the values from a Tkinter Entry.

what am i doing wrong with tkinter application for email slicer?

making an email slicer,
some errors I'm getting are:
AttributeError: 'bool' object has no attribute 'index'
ValueError: substring not found
now, with this specific code, I'm getting no result at all, it just doesn't do anything when I click the button
root = Tk()
e = Entry(root)
e.grid(row = 6, column = 6)
s = Label(root)
s.grid(row = 1, column = 1)
wel = Label(root, text = "whats your email")
wel.grid(row = 1, column = 5)
inp = Entry(root)
inp.grid(row = 3, column = 5)
def callback(re = inp.get()):
us = re[:re.startswith("#")]
uss = re[re.startswith("#")+1:]
var = StringVar()
var.set(us + uss)
sub = Button(root, text = "submit", command = lambda:callback())
sub.grid(row = 5, column = 5)
final = Label(root, textvariable = var)
final.grid(row = 5, column = 6)
root.mainloop()
You need to
call inp.get() inside callback(), not as default value of argument
use find() instead of startswith()
call var.set(...) inside the function as well
def callback():
re = inp.get()
pos = re.find("#")
if pos >= 0:
user = re[:pos]
domain = re[pos+1:]
var.set(user+","+domain)
else:
var.set("Invalid email")
Note that above is not enough to check whether the input email is valid or not, for example two "#" in the input.

Changing a variable with a button (and a function) in Python Tkinter

I have the following code (it's partly in Dutch but I don't think that'll be an issue):
from tkinter import *
root = Tk()
woorden_en = ["mouse", "armadillo", "caterpillar", "buffalo", "dragonfly", "eel", "monkey", "lark", "manatee", "squid"]
woorden_nl = ["muis", "gordeldier", "rups", "buffel", "libelle", "paling", "aap", "leeuwerik", "zeekoe", "inktvis"]
nummer = IntVar()
nlWoord = StringVar()
enWoord = StringVar()
goedfout = StringVar()
def vorige():
nummer -= 1
def volgende():
nummer += 1
def controleer():
print("Correct!")
secondGrid = Frame(root)
secondGrid.grid(row = 2, column = 1, columnspan = 2)
labelVertaling = Label(root, text="vertaling")
textVertaling = Entry(root, width=30, textvariable = nlWoord)
runVorige = Button(secondGrid, text="vorige", command = vorige)
runVolgende = Button(secondGrid, text="volgende", command = volgende)
runControleer = Button(secondGrid, text="controleer", command = controleer)
labelWoord = Label(root, text="woord")
labelWoordEn = Label(root, textvariable = enWoord)
labelNo = Label(root, textvariable = nummer)
Correct = Label(root, textvariable = goedfout)
Correct.grid(row = 2, column = 0)
labelNo.grid(row = 0, column = 0)
labelWoord.grid(row = 0, column = 1)
labelWoordEn.grid(row = 1, column = 1)
labelVertaling.grid(row = 0, column = 2)
textVertaling.grid(row = 1, column = 2)
runVorige.grid(row = 0, column = 0, sticky = "W")
runVolgende.grid(row = 0, column = 1, sticky = "W")
runControleer.grid(row = 0, column = 2, sticky = "W")
nummer.set(1)
enWoord.set(woorden_en[0])
root.mainloop()
The start value of 'nummer' is 1, as set in the 3rd to last line. This value needs to be changed with either -1 or +1 when clicking buttons 'vorige' (previous) or 'volgende' (next). The current code in the functions give me erros. Apparently I need to use set/get functions, but I cannot find out how to make it work. Any input or help would be appreciated.
Just change your function to:
def vorige():
nummer_val = nummer.get()
nummer_val -= 1
nummer.set(nummer_val)
def volgende():
nummer_val = nummer.get()
nummer_val += 1
nummer.set(nummer_val)
This is because nummer is an IntVar() and you have to get the value of the variable using get() method. After that you have to assign it to a variable and then reduce/increase its value by 1. The first error you were getting was because the nummer was not globalized inside the function, but that was not the approach you should have taken, anyway this should fix your errors.

updating tkinter window for text background changes

Trying to show a green or red background in the text field of the answer to the simple addition quizzer.
Currently in PyCHarm complains that:
Entry.grid_configure(background = "red")
TypeError: grid_configure() missing 1 required positional argument: 'self'
0
I can't seem to figure this out. Any help is appreciated.
Here's the code so far:
from tkinter import *
import random
class MainGUI:
def __init__(self):
window = Tk() # Create the window
window.title("Addition Quizzer") # Set the title
#window.width(len(window.title()))
self.number1 = random.randint(0, 9)
self.number2 = random.randint(0, 9)
Label(window, text = "+").grid(row = 2, column = 1, sticky = E)
Label(window, text = "Answer").grid(row = 3, column = 1, sticky = W)
self.firstNumber = StringVar()
Label(window, text = self.number1, justify = RIGHT).grid(row = 1, column = 2)
self.secondNumber = StringVar()
Label(window, text = self.number2, justify = RIGHT).grid(row = 2, column = 2)
self.entry = StringVar()
Entry(window, textvariable = self.entry, justify = CENTER, width = 4, background = "grey").grid(row = 3, column = 2)
Button(window, text = "Answer:", command = self.computeAnswer).grid(row = 4, column = 1, sticky = E)
self.result = StringVar()
Label(window, textvariable = self.result).grid(row = 4, column = 2)
window.mainloop() # Create the event loop
def computeAnswer(self):
self.result.set(format(self.number1 + self.number2))
if self.entry == self.result:
self.displayCorrect()
else:
self.displayIncorrect()
def displayCorrect(self):
# self.correctAnswer = "Correct"
# Label(self.window, text = self.correctAnswer, background = "green", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "green")
def displayIncorrect(self):
# self.incorrectAnswer = "Incorrect"
# Label(self.window, text = self.incorrectAnswer, background = "red", justify = RIGHT).grid(row = 5, column = 2)
Entry.grid_configure(background = "red")
MainGUI()
If you had read and followed this in the Help Center material, you would have reduced your code to the following, which still gets the same error message.
from tkinter import *
Entry.grid_configure()
The message refers to the fact that Python instance methods require an instance. This is usually done by calling the method on an instance instead of the class. Otherwise, an instance must be given as the first argument. Consider
mylist = []
mylist.append(1)
list.append(mylist, 2)
print(mylist)
# [1, 2]
You need to save a reference to your Entry box. Change
Entry(window, ..., background = "grey").grid(...)
to
self.entry = Entry(window, ..., background = "grey").grid(...)
I do not know if calling .grid_configure(background=color will do what you want.
This will, I am sure.
self.entry['background'] = 'red'

Tkinter bug with Python 3.3.3

I have created a mad libs program and the window shows up as just an empty GUI and after I shut it down the tkinter code comes up saying that tk has no attribute. Here is my Program.
from tkinter import *
class Application(Frame):
'''GUI that takes a user's input in order to create a story'''
def initializeFrame(self, master):
'''Initializes the frame'''
#superclass constructor
super(Application, self).initializeFrame(master)
self.grid()
self.createWidgets()
def createWidgets(self):
'''Creates some widgets that get the information of the story and display it'''
#instruction label
Label(self,
text = "Enter story information"
).grid(row = 0, column = 0, columnspan = 2, sticky = W)
Label(self,
text = "Person: "
).grid(row = 1, column = 0, sticky = W)
self.personEntry = Entry(self)
self.PersonEntry.grid(row = 1, column = 1, sticky = W)
Label(self,
text = "Plural Noun:"
).grid(row = 2, column = 0, sticky = W)
self.nounEntry = Entry(self)
self.nounEntry.grid(row = 2, column = 1, sticky = W)
Label(self,
text = "Verb:"
).grid(row = 3, column = 0, sticky = W)
self.verbEntry = Entry(self)
self.verbEntry.grid(row = 3, column = 1, sticky = W)
Label(self,
text = "Adjective:"
).grid(row = 4, column = 0, sticky = W)
self.isItchy = BooleanVar()
Checkbutton(self,
text = "itchy",
variable = self.isItchy
).grid(row = 4, column = 1, sticky = W)
self.isJoyous = BooleanVar()
Checkbutton(self,
text = "joyous",
variable = self.isJoyous
).grid(row = 4, column = 2, sticky = W)
self.isElectric = BooleanVar()
Checkbutton(self,
text = "electric",
variable = self.isElectric
).grid(row = 4, column = 3, sticky = W)
Label(self,
text = "Body Part:"
).grid(row = 5, column = 0, sticky = W)
self.bodyPart = StringVar()
self.bodyPart.set(None)
bodyParts = ["gluteus maximus", "platella", "Cranium"]
column = 1
for part in bodyParts:
Radiobutton(self,
text = part,
variable = self.bodyPart,
value = part
).grid(row = 5, column = column, sticky = W)
column += 1
Button(self,
text = "Click for the story",
command = self,
).grid(row = 6, column = 0, sticky = W)
self.storyTxt = Text(self, width = 75, height = 10, wrap = WORD)
self.storyTxt.grid(row = 7, column = 0, columnspan = 4)
def tellStory(self):
'''Prints the new story based on the users inputs'''
person = self.personEntry.get()
noun = self.nounEntry.get()
verb = self.verbEntry.get()
adjectives = ""
if self.isItchy.get():
adjectives +='itchy, '
if self.isJoyous.get():
adjectives += 'itchy, '
if self.isElectric.get():
adjectives += 'electric, '
bodyPart = self.bodyPart.get()
#the story
story = "The famous explorer"
story += person
story = "had nearly given up on a life-long quest to find The Lost City of"
story += noun.title()
story = "when one day, the"
story += noun
story += "found"
story += person + "."
story += "A strong"
story += adjectives
story += "peculiar feeling overwhelmed the explorer. "
story += "After all this time, the quest was finally over. A tear came to "
story += person + "'s"
story += bodyPart + ". "
story += "And then, the "
story += noun
story += "promptly devoured "
story += person + "."
story += "The moral of this story? Be careful what you "
story += verb
story += "for. "
self.storyTxt.delete(0.0, END)
self.storyTxt.insert(0.0, story)
root = Tk()
root.title("Mad Libs")
app = Application(root)
root.mainloop()
Application(Frame)
createWidgets(self)
And here is the part of the tkinter program that has an error
class BaseWidget(Misc):
"""Internal class."""
def _setup(self, master, cnf):
"""Internal function. Sets up information about children."""
if _support_default_root:
global _default_root
if not master:
if not _default_root:
_default_root = Tk()
master = _default_root
self.master = master
self.tk = master.tk
name = None
if 'name' in cnf:
name = cnf['name']
del cnf['name']
if not name:
name = repr(id(self))
self._name = name
if master._w=='.':
self._w = '.' + name
else:
self._w = master._w + '.' + name
self.children = {}
if self._name in self.master.children:
self.master.children[self._name].destroy()
self.master.children[self._name] = self
def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
"""Construct a widget with the parent widget MASTER, a name WIDGETNAME
and appropriate options."""
if kw:
cnf = _cnfmerge((cnf, kw))
self.widgetName = widgetName
BaseWidget._setup(self, master, cnf)
if self._tclCommands is None:
self._tclCommands = []
classes = [(k, v) for k, v in cnf.items() if isinstance(k, type)]
for k, v in classes:
del cnf[k]
self.tk.call(
(widgetName, self._w) + extra + self._options(cnf))
for k, v in classes:
k.configure(self, v)
def destroy(self):
"""Destroy this and all descendants widgets."""
for c in list(self.children.values()): c.destroy()
self.tk.call('destroy', self._w)
if self._name in self.master.children:
del self.master.children[self._name]
Misc.destroy(self)
def _do(self, name, args=()):
# XXX Obsolete -- better use self.tk.call directly!
return self.tk.call((self._w, name) + args)
This is not a bug in tkinter, it is a bug in your code.
root.mainloop() needs to be the last line of code executed in your program. It will not return ultil the main window is destroyed. You have code after that which tries to create more windows. Since the main window has been destroyed, you get the error that you report.
Your window is likely coming up blank because you call self.createWidgets but that class has no such method. Perhaps this is a typo in your code, since you do define a function with that name, but outside the scope of the class.

Resources