Tkinter validation of Entry widgets in Python - python

I want to know how the inputs to an entry widget in tkinter is validated so as to accommodate only one digit and (+/-) symbols. This widget is going to accept charge of the atom from the user.
Add in this code below:
class Onlyonedigit(ttk.Entry):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.configure(
validate='all',
validatecommand=(self.register(self.validate_digit), '%P'),
)
def validate_digit(self, input):
if input.isdigit():
return True
else:
return False

1. Solution Steps
Import tkinter module
import tkinter
Import tkinter submodules
from tkinter import * # (*) Asterisk symbol means import everything
Define the callback function
def callback(input):
if input.isdigit():
print(input)
return True
elif input is "":
print(input)
return True
else:
print(input)
return False
Explanation:
The callback function checks the input in the Entry widget for valid entry. If
the entry is valid it returns True else False. In this example, the input
entered through the keyboard is checked for numeric type. If the input is
numeric type then the callback function returns true. For deletion operation
the callback function returns true as input is “” . However for any non-
numeric input the callback function returns false.
Creating the parent window
root=Tk()
Creating Entry widget
e=Entry(root)
Specify the position of Entry widget within the parent window
e.place(x=50, y=50)
Register the callback function
reg=root.register(callback)
Call the callback function to validate the input in Entry widget
e.config(validate="key", validatecommand=(reg, '%P'))
Final step : Run the application
root.mainloop()
Hope it works. Source : Python Tkinter - Validating Entry Widget -GeeksforGeeks
2. Complete code:
import tkinter
from tkinter import *
def callback(input):
if input.isdigit():
print(input)
return True
elif input is "":
print(input)
return True
else:
print(input)
return False
root = Tk()
e = Entry(root)
e.place(x = 50, y = 50)
reg = root.register(callback)
e.config(validate ="key",
validatecommand =(reg, '% P'))
root.mainloop()

Related

Validating a tk entry after a value is entered [duplicate]

What is the recommended technique for interactively validating content in a tkinter Entry widget?
I've read the posts about using validate=True and validatecommand=command, and it appears that these features are limited by the fact that they get cleared if the validatecommand command updates the Entry widget's value.
Given this behavior, should we bind on the KeyPress, Cut, and Paste events and monitor/update our Entry widget's value through these events? (And other related events that I might have missed?)
Or should we forget interactive validation altogether and only validate on FocusOut events?
The correct answer is, use the validatecommand attribute of the widget. Unfortunately this feature is severely under-documented in the Tkinter world, though it is quite sufficiently documented in the Tk world. Even though it's not documented well, it has everything you need to do validation without resorting to bindings or tracing variables, or modifying the widget from within the validation procedure.
The trick is to know that you can have Tkinter pass in special values to your validate command. These values give you all the information you need to know to decide on whether the data is valid or not: the value prior to the edit, the value after the edit if the edit is valid, and several other bits of information. To use these, though, you need to do a little voodoo to get this information passed to your validate command.
Note: it's important that the validation command returns either True or False. Anything else will cause the validation to be turned off for the widget.
Here's an example that only allows lowercase. It also prints the values of all of the special values for illustrative purposes. They aren't all necessary; you rarely need more than one or two.
import tkinter as tk # python 3.x
# import Tkinter as tk # python 2.x
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# valid percent substitutions (from the Tk entry man page)
# note: you only have to register the ones you need; this
# example registers them all for illustrative purposes
#
# %d = Type of action (1=insert, 0=delete, -1 for others)
# %i = index of char string to be inserted/deleted, or -1
# %P = value of the entry if the edit is allowed
# %s = value of entry prior to editing
# %S = the text string being inserted or deleted, if any
# %v = the type of validation that is currently set
# %V = the type of validation that triggered the callback
# (key, focusin, focusout, forced)
# %W = the tk name of the widget
vcmd = (self.register(self.onValidate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(self, validate="key", validatecommand=vcmd)
self.text = tk.Text(self, height=10, width=40)
self.entry.pack(side="top", fill="x")
self.text.pack(side="bottom", fill="both", expand=True)
def onValidate(self, d, i, P, s, S, v, V, W):
self.text.delete("1.0", "end")
self.text.insert("end","OnValidate:\n")
self.text.insert("end","d='%s'\n" % d)
self.text.insert("end","i='%s'\n" % i)
self.text.insert("end","P='%s'\n" % P)
self.text.insert("end","s='%s'\n" % s)
self.text.insert("end","S='%s'\n" % S)
self.text.insert("end","v='%s'\n" % v)
self.text.insert("end","V='%s'\n" % V)
self.text.insert("end","W='%s'\n" % W)
# Disallow anything but lowercase letters
if S == S.lower():
return True
else:
self.bell()
return False
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
For more information about what happens under the hood when you call the register method, see Why is calling register() required for tkinter input validation?
For the canonical documentation see the Validation section of the Tcl/Tk Entry man page
After studying and experimenting with Bryan's code, I produced a minimal version of input validation. The following code will put up an Entry box and only accept numeric digits.
from tkinter import *
root = Tk()
def testVal(inStr,acttyp):
if acttyp == '1': #insert
if not inStr.isdigit():
return False
return True
entry = Entry(root, validate="key")
entry['validatecommand'] = (entry.register(testVal),'%P','%d')
entry.pack()
root.mainloop()
Perhaps I should add that I am still learning Python and I will gladly accept any and all comments/suggestions.
Use a Tkinter.StringVar to track the value of the Entry widget. You can validate the value of the StringVar by setting a trace on it.
Here's a short working program that accepts only valid floats in the Entry widget.
try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2
root = Tk()
sv = StringVar()
def validate_float(var):
new_value = var.get()
try:
new_value == '' or float(new_value)
validate_float.old_value = new_value
except:
var.set(validate_float.old_value)
validate_float.old_value = '' # Define function attribute.
# trace wants a callback with nearly useless parameters, fixing with lambda.
sv.trace('w', lambda nm, idx, mode, var=sv: validate_float(var))
ent = Entry(root, textvariable=sv)
ent.pack()
ent.focus_set()
root.mainloop()
Bryan's answer is correct, however no one mentioned the 'invalidcommand' attribute of the tkinter widget.
A good explanation is here:
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html
Text copy/pasted in case of broken link
The Entry widget also supports an invalidcommand option that specifies a callback function that is called whenever the validatecommand returns False. This command may modify the text in the widget by using the .set() method on the widget's associated textvariable. Setting up this option works the same as setting up the validatecommand. You must use the .register() method to wrap your Python function; this method returns the name of the wrapped function as a string. Then you will pass as the value of the invalidcommand option either that string, or as the first element of a tuple containing substitution codes.
Note:
There is only one thing that I cannot figure out how to do: If you add validation to an entry, and the user selects a portion of the text and types a new value, there is no way to capture the original value and reset the entry. Here's an example
Entry is designed to only accept integers by implementing 'validatecommand'
User enters 1234567
User selects '345' and presses 'j'. This is registered as two actions: deletion of '345', and insertion of 'j'. Tkinter ignores the deletion and acts only on the insertion of 'j'. 'validatecommand' returns False, and the values passed to the 'invalidcommand' function are as follows: %d=1, %i=2, %P=12j67, %s=1267, %S=j
If the code does not implement an 'invalidcommand' function, the 'validatecommand' function will reject the 'j' and the result will be 1267. If the code does implement an 'invalidcommand' function, there is no way to recover the original 1234567.
Define a function returning a boolean that indicates whether the input is valid.Register it as a Tcl callback, and pass the callback name to the widget as a validatecommand.
For example:
import tkinter as tk
def validator(P):
"""Validates the input.
Args:
P (int): the value the text would have after the change.
Returns:
bool: True if the input is digit-only or empty, and False otherwise.
"""
return P.isdigit() or P == ""
root = tk.Tk()
entry = tk.Entry(root)
entry.configure(
validate="key",
validatecommand=(
root.register(validator),
"%P",
),
)
entry.grid()
root.mainloop()
Reference.
While studying Bryan Oakley's answer, something told me that a far more general solution could be developed. The following example introduces a mode enumeration, a type dictionary, and a setup function for validation purposes. See line 48 for example usage and a demonstration of its simplicity.
#! /usr/bin/env python3
# https://stackoverflow.com/questions/4140437
import enum
import inspect
import tkinter
from tkinter.constants import *
Mode = enum.Enum('Mode', 'none key focus focusin focusout all')
CAST = dict(d=int, i=int, P=str, s=str, S=str,
v=Mode.__getitem__, V=Mode.__getitem__, W=str)
def on_validate(widget, mode, validator):
# http://www.tcl.tk/man/tcl/TkCmd/ttk_entry.htm#M39
if mode not in Mode:
raise ValueError('mode not recognized')
parameters = inspect.signature(validator).parameters
if not set(parameters).issubset(CAST):
raise ValueError('validator arguments not recognized')
casts = tuple(map(CAST.__getitem__, parameters))
widget.configure(validate=mode.name, validatecommand=[widget.register(
lambda *args: bool(validator(*(cast(arg) for cast, arg in zip(
casts, args)))))]+['%' + parameter for parameter in parameters])
class Example(tkinter.Frame):
#classmethod
def main(cls):
tkinter.NoDefaultRoot()
root = tkinter.Tk()
root.title('Validation Example')
cls(root).grid(sticky=NSEW)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
def __init__(self, master, **kw):
super().__init__(master, **kw)
self.entry = tkinter.Entry(self)
self.text = tkinter.Text(self, height=15, width=50,
wrap=WORD, state=DISABLED)
self.entry.grid(row=0, column=0, sticky=NSEW)
self.text.grid(row=1, column=0, sticky=NSEW)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
on_validate(self.entry, Mode.key, self.validator)
def validator(self, d, i, P, s, S, v, V, W):
self.text['state'] = NORMAL
self.text.delete(1.0, END)
self.text.insert(END, 'd = {!r}\ni = {!r}\nP = {!r}\ns = {!r}\n'
'S = {!r}\nv = {!r}\nV = {!r}\nW = {!r}'
.format(d, i, P, s, S, v, V, W))
self.text['state'] = DISABLED
return not S.isupper()
if __name__ == '__main__':
Example.main()
import tkinter
tk=tkinter.Tk()
def only_numeric_input(e):
#this is allowing all numeric input
if e.isdigit():
return True
#this will allow backspace to work
elif e=="":
return True
else:
return False
#this will make the entry widget on root window
e1=tkinter.Entry(tk)
#arranging entry widget on screen
e1.grid(row=0,column=0)
c=tk.register(only_numeric_input)
e1.configure(validate="key",validatecommand=(c,'%P'))
tk.mainloop()
#very usefull for making app like calci
Here's an improved version of #Steven Rumbalski's answer of validating the Entry widgets value by tracing changes to a StringVar — which I have already debugged and improved to some degree by editing it in place.
The version below puts everything into a StringVar subclass to encapsulates what's going on better and, more importantly allow multiple independent instances of it to exist at the same time without interfering with each other — a potential problem with his implementation because it utilizes function attributes instead of instance attributes, which are essentially the same thing as global variables and can lead to problems in such a scenario.
try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2
class ValidateFloatVar(StringVar):
"""StringVar subclass that only allows valid float values to be put in it."""
def __init__(self, master=None, value=None, name=None):
StringVar.__init__(self, master, value, name)
self._old_value = self.get()
self.trace('w', self._validate)
def _validate(self, *_):
new_value = self.get()
try:
new_value == '' or float(new_value)
self._old_value = new_value
except ValueError:
StringVar.set(self, self._old_value)
root = Tk()
ent = Entry(root, textvariable=ValidateFloatVar(value=42.0))
ent.pack()
ent.focus_set()
ent.icursor(END)
root.mainloop()
This code can help if you want to set both just digits and max characters.
from tkinter import *
root = Tk()
def validate(P):
if len(P) == 0 or len(P) <= 10 and P.isdigit(): # 10 characters
return True
else:
return False
ent = Entry(root, validate="key", validatecommand=(root.register(validate), '%P'))
ent.pack()
root.mainloop()
Responding to orionrobert's problem of dealing with simple validation upon substitutions of text through selection, instead of separate deletions or insertions:
A substitution of selected text is processed as a deletion followed by an insertion. This may lead to problems, for example, when the deletion should move the cursor to the left, while a substitution should move the cursor to the right. Fortunately, these two processes are executed immediately after one another.
Hence, we can differentiate between a deletion by itself and a deletion directly followed by an insertion due to a substitution because the latter has does not change the idle flag between deletion and insertion.
This is exploited using a substitutionFlag and a Widget.after_idle().
after_idle() executes the lambda-function at the end of the event queue:
class ValidatedEntry(Entry):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tclValidate = (self.register(self.validate), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
# attach the registered validation function to this spinbox
self.config(validate = "all", validatecommand = self.tclValidate)
def validate(self, type, index, result, prior, indelText, currentValidationMode, reason, widgetName):
if typeOfAction == "0":
# set a flag that can be checked by the insertion validation for being part of the substitution
self.substitutionFlag = True
# store desired data
self.priorBeforeDeletion = prior
self.indexBeforeDeletion = index
# reset the flag after idle
self.after_idle(lambda: setattr(self, "substitutionFlag", False))
# normal deletion validation
pass
elif typeOfAction == "1":
# if this is a substitution, everything is shifted left by a deletion, so undo this by using the previous prior
if self.substitutionFlag:
# restore desired data to what it was during validation of the deletion
prior = self.priorBeforeDeletion
index = self.indexBeforeDeletion
# optional (often not required) additional behavior upon substitution
pass
else:
# normal insertion validation
pass
return True
Of course, after a substitution, while validating the deletion part, one still won’t know whether an insert will follow.
Luckily however, with:
.set(),
.icursor(),
.index(SEL_FIRST),
.index(SEL_LAST),
.index(INSERT),
we can achieve most desired behavior retrospectively (since the combination of our new substitutionFlag with an insertion is a new unique and final event.

Allowing only a range of inputs within an entry field in Tkinter

I need to implement an entry box that accepts only a range of DoubleVar values. I have referenced this question that was asked How to only allow certain parameters within an entry field on Tkinter, but I want the user to be notified (using a change in font colour or anything) while they are entering the values. I've read the documentation but this is something that I haven't come across.
I'm new to Tkinter, so please excuse me if this sounds very stupid
You can bind KeyRelease event of the Entry to a callback and check whether the input value is valid and within the required range, then update the foreground color of the Entry accordingly:
import tkinter as tk
root = tk.Tk()
def check_value(entry, min_value, max_value):
try:
value = float(entry.get().strip())
valid = min_value <= value <= max_value
except ValueError:
valid = False
entry.config(fg='black' if valid else 'red')
return valid # in case you want the checking result somewhere else
entry = tk.Entry(root)
entry.pack()
entry.bind('<KeyRelease>', lambda e: check_value(e.widget, 10, 20))
root.mainloop()
Use the validatecommand option for the Entry.
Some code fragments to show how it works:
root = tk.Tk()
vcmd = root.register(is_number)
e = ttk.Entry(pressf, justify='right', validate='key', validatecommand=(vcmd, '%P'))
def is_number(data):
"""Validate the contents of an entry widget as a float."""
if data == '':
return True
try:
rv = float(data)
if rv < 0:
return False
except ValueError:
return False
return True
This basically calls the validation function on every keypress. Only if the validation succeeds is the character added to the entry.
You can find a complete working example here.
Edit
Above is the "canonical" example of a validator. It allows or disallows characters into the Entry.
But you can also use it in other ways.
For example, you can always return True, but e.g. change the text color of the Entry to red if the value is not within the limits you want.

What caused the error

I am trying to make GUI that reverses the user input but something is wrong
from tkinter import *
from tkinter.ttk import *
def reverse(s):
s=U.get()
return s[::-1]
root=Tk(className="Reverse your text")
la=Label(root, text="Enter text to reverse")
la.pack()
U=Entry(root,textvariable=s)
U.pack()
BT=Button(root, text="reverse", command=reverse(s))
BT.pack()
root.mainloop()
Error: U=Entry(root,textvariable=s)
NameError: name 's' is not defined
def reverse(s): should not have an s if you don't intend to pass any arguments to the function. Likewise for command=reverse(s)
U=Entry(root,textvariable=s) does not need a textvariable if you're just going to access the Entry's value directly with .get. And anyway, you can't use s here, because you never assigned a StringVar object to s to begin with.
The value returned by return s[::-1] will not be visible in any way to the user. If you want to show the reversed string, you need to print it or insert it into the entry, or similar.
from tkinter import *
from tkinter.ttk import *
def reverse():
s=U.get()
U.delete(0, END)
U.insert(0,s[::-1])
root=Tk(className="Reverse your text")
la=Label(root, text="Enter text to reverse")
la.pack()
U=Entry(root)
U.pack()
BT=Button(root, text="reverse", command=reverse)
BT.pack()
root.mainloop()
Result:

How to return the value of a variable only when a button has been clicked?

My code:
def file_exists(f_name):
select = 0
def skip():
nonlocal select
select = 1
err_msg.destroy()
def overwrite():
nonlocal select
select = 2
err_msg.destroy()
def rename():
global select
select = 3
err_msg.destroy()
# Determine whether already existing zip member's name is a file or a folder
if f_name[-1] == "/":
target = "folder"
else:
target = "file"
# Display a warning message if a file or folder already exists
''' Create a custom message box with three buttons: skip, overwrite and rename. Depending
on the users change the value of the variable 'select' and close the child window'''
if select != 0:
return select
I know using nonlocal is evil but I have to go on with my procedural approach, at least for this program.
The problem is when I call this function it rushes through and returns the initial value of select (which is 0) immediately, no matter which button I've pressed. When I press a button, the value of select will change accordingly.
So how can I return it only after a button has been pressed? As you can see, my first attempt was to return the value only when select is != 0 but this doesn't work.
Thanks for your suggestions!
You can make use of the .update() function to block without freezing the GUI. Basically you call root.update() in a loop until a condition is fulfilled. An example:
def block():
import Tkinter as tk
w= tk.Tk()
var= tk.IntVar()
def c1():
var.set(1)
b1= tk.Button(w, text='1', command=c1)
b1.grid()
def c2():
var.set(2)
b2= tk.Button(w, text='2', command=c2)
b2.grid()
while var.get()==0:
w.update()
w.destroy()
return var.get()
print(block())

TypeError and AttibuteError on Temperature Converter GUI in Python

I am trying to understand the codes wrote by someone else so that I can build my own GUI.
Here is the code.
MODEL: temperatureconvert.py
class TemperatureConvert:
"""
class TemperatureCovert is the MODEL for a simple program. It just converts temperature
in Celisus into Fahrenheit and vice versa.
"""
def __init__(self):
self.fahrenheitEntrySpace = 0
self.celsiusEntrySpace = 0
def convertTempF2C(self):
fahrenheit = self.fahrenheitEntrySpace
if fahrenheit != 0.0:
celsius = (fahrenheit - 32) * 5 / 9
else:
celsius = -17.7777778
def convertTempC2F(self):
celsius = self.celsiusEntrySpace
if celsius != 0.0:
fahrenheit = (celsius * 9.0/5.0 + 32)
else:
fahrenheit = 32
def __str__(self):
return str(self.counter)
VIEW: myFrame10.py
import tkinter
class MyFrame(tkinter.Frame): #creates window for controls in an object made
#from a class called "tkinter.Frame"
"""
Class myFrame is the VIEW for a simple program that contains two buttons, two entry areas, and four labels:
one button a converter;
one button quits the program;
one entry is for celsius;
one entry is for fahrenheit;
and the labels prompt user for input, and label the entry values as needed.
"""
def __init__(self, controller):
"""
places the controls on the frame
"""
tkinter.Frame.__init__(self) #initilizes the superclass
self.pack() #required for the buttons to show up properly.
self.controller = controller #saves ref to controller to call methods on
#contoller object when user generates events
#Fahrenheit Input Prompt
self.fahrenheitLabel = tkinter.Label(self)
self.fahrenheitLabel["text"] = "Enter Fahrenheit Value:"
self.fahrenheitLabel.pack({"side":"left"})
#Fahrenheit Entry Space
self.fahrenheitEntrySpace = tkinter.Entry(self)
self.fahrenheitEntrySpace.insert(0, "FahrenheitTemperature")
self.fahrenheitEntrySpace.pack({"side":"left"})
#Fahrenheit Value label
self.fahrenheitLabel = tkinter.Label(self)
self.fahrenheitLabel["text"] = ("Fahrenheit Degrees")
self.fahrenheitLabel.pack({"side":"left"})
#Converter button
self.convertButton=tkinter.Button(self)
self.convertButton["text"]= "Convert"
self.convertButton["command"]=self.controller.buttonPressed
# an object that remembers both self and reply when later called
self.convertButton.pack({"side":"left"})
#Quit button
self.quitButton = tkinter.Button(self)
self.quitButton["text"] = "QUIT"
self.quitButton["command"] = self.quit
#the statement above attaches the event handler
#self.quit() to the quit button
self.quitButton.pack({"side":"right"})
#Celsius Value label
self.celsiusLabel = tkinter.Label(self)
self.celsiusLabel["text"] = ("Celsius Degrees")
self.celsiusLabel.pack({"side":"right"})
#Celsius Entry Space
self.celsiusEntrySpace = tkinter.Entry(self)
self.celsiusEntrySpace.insert(0, "CelsiusTemperature")
self.celsiusEntrySpace.pack({"side":"right"})
#Celsius Input Prompt
self.celsiusLabel = tkinter.Label(self)
self.celsiusLabel["text"] = ("Enter Celsius Value:")
self.celsiusLabel.pack({"side":"right"})
CONTROLLER: controller10.py
import tkinter
import myFrame10 #the VIEW
import temperatureconvert #the MODEL
class Controller:
"""
The CONTROLLER for an app that follows the MODEL/VIEW/CONTROLLER architecture.
When the user presses a button on the VIEW,
this controller calls the appropriate methods in the model.
The controller handles all the communication between the model and the view.
"""
def __init__(self):
"""
This starts the TK framework up;
instantiates the model;
instantiates the VIEW;
and states the event loop that waits for the user to press a button on the view
"""
root = tkinter.Tk() #This starts the TK framework up;
self.model = temperatureconvert.TemperatureConvert() #instantiates the model
self.view = myFrame10.MyFrame(self) #instantiates the VIEW
self.view.mainloop() # states event loop waits for user to press button on view
root.destroy() #lets user quit
def buttonPressed(self):
"""
Convert F --> C
"""
self.model.convertTempF2C(self.view.fahrenheitEntrySpace.get)
#MODEL creates new celsius temp from(fahrenheit input)
self.view.celsiusEntrySpace.clear()
#replaces VIEW's old default celsius value
self.view.celsiusEntrySpace.insert(self.model.celsius)
#and insert's MODEL's newly converted (celsius) value
"""
Convert C --> F
"""
self.model.convertTempC2F(self.view.celsiusEntrySpace.get)
#MODEL creates new fahrenheit temp from (celsius input)
self.view.fahrenheitEntrySpace.pop()
#replaces VIEW's old default 0 fahrenheit value
self.view.fahrenheitEntrySpace.insert(self.model.fahrenheit)
#and insert's MODEL's newly converted (fahrenheit) value
if __name__=="__main__":
c = Controller()
First of all, I got an TypeError as below.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/tkinter/__init__.py", line 1533, in __call__
return self.func(*args)
File "/Users/adrian/Desktop/10X Genomics/Python exercise/controller10.py", line 36, in buttonPressed
self.model.convertTempF2C(self.view.fahrenheitEntrySpace.get)
TypeError: convertTempF2C() takes 1 positional argument but 2 were given
However, I don't know what part goes wrong under method of convertTempF2c().
Second, how could I remove the "celsiusEntrySpace" default value (in this case, it is CelsiusTemperature) with the new value obtain from converting Fahrenheit? .pop is not working in this case since it only works on the list, and it gave the error below.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/tkinter/__init__.py", line 1533, in __call__
return self.func(*args)
File "/Users/adrian/Desktop/10X Genomics/Python exercise/controller10.py", line 39, in buttonPressed
self.view.celsiusEntrySpace.pop()
AttributeError: 'Entry' object has no attribute 'pop'
So, how could I fix those 2 errors above? Please help! Thanks~
convertTempF2C looks like the following; notice that it takes no positional parameters other than self:
def convertTempF2C(self):
However, you're calling it like this:
self.model.convertTempF2C(self.view.fahrenheitEntrySpace.get)
There are two things wrong with that line. First, you're passing an extra parameter which the function isn't expecting. Second, you're not actually calling the get function, you're merely referencing it. You should call it like this:
value = self.view.fahrenheitEntrySpace.get()
self.model.convertTempF2C(value)
Note the trailing parenthesis on that first line.
Why two steps instead of one? It makes the code clearer, and it makes it easier to debug because you can print out the value before doing the conversion to verify it is what you think it is.
Finally, you are doing this:
self.view.celsiusEntrySpace.pop()
Just as the error message is telling you, self.view.celsiusEntrySpace is an Entry widget, and Entry widgets don't have a pop method. I don't know why you think it does, unless you're going by some incorrect documentation. I also don't quite understand what you think this is doing, so I can't offer an alternative.

Categories