AttributeError when using validatecommand on a Spinbox - python

I am doing a test writing a script that validate a spinbox to implement it on a larger system, but I am struggling with python because it is warning that there is no spinbox attribute on my Window class. Check my code:
from Tkinter import *
class Window:
def __init__(self, toplevel):
self.spinbox = Spinbox(toplevel, from_ = 0, to = 10,
validate = 'all', validatecommand = self.validate)
self.spinbox.pack()
def validate(self):
print self.spinbox.get()
root = Tk()
Window(root)
root.mainloop()
This is the error it is giving:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1486, in __call__
return self.func(*args)
File "D:\DOCS\FILIPE\PROGRAMMING\PYTHON\Tkinter sandbox\01 - spinbox validate.
py", line 13, in validate
print self.spinbox.get()
AttributeError: Window instance has no attribute 'spinbox'
Anyone could help me with this one?

If you add some print statements to your code:
class Window:
def __init__(self, toplevel):
print "A start", self, self.__dict__
self.spinbox = Spinbox(toplevel, from_ = 0, to = 10,
validate = 'all', validatecommand = self.validate)
self.spinbox.pack()
print "A end", self, self.__dict__
def validate(self):
print "B", self, self.__dict__
print self.spinbox.get()
#...
You get the output:
A start <__main__.Window instance at 0x7fe4f8deec20> {}
B <__main__.Window instance at 0x7fe4f8deec20> {}
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1532, in __call__
return self.func(*args)
File "t.py", line 14, in validate
print self.spinbox.get()
AttributeError: Window instance has no attribute 'spinbox'
A end <__main__.Window instance at 0x7fe4f8deec20> {'spinbox': <Tkinter.Spinbox instance at 0x7fe4f8e0da28>}
This means that the validate function is called inside the constructor to validate the initial value, and at that point self.spinbox has not yet been set. You will need to either check if you are still constructing or delay setting validatecommand like this:
self.spinbox = Spinbox(toplevel, from_ = 0, to = 10,
validate = 'all')
self.spinbox["validatecommand"] = self.validate
self.spinbox.pack()

I MANAGED TO SOLVE THE PROBLEM USING TEXTVARIABLES AND THE TRACE METHOD
#Validates
self.countstringvar = StringVar()
self.countstringvar.trace("w", lambda name, index, mode,
sv = self.countstringvar: self.noLettersValidate(sv, self.count))
self.starsstringvar = StringVar()
self.starsstringvar.trace("w", lambda name, index, mode,
sv = self.starsstringvar: self.noLettersValidate(sv, self.stars))
self.scorestringvar = StringVar()
self.scorestringvar.trace("w", lambda name, index, mode,
sv = self.scorestringvar: self.noLettersValidate(sv, self.score))
self.count['textvariable'] = self.countstringvar
self.stars['textvariable'] = self.starsstringvar
self.score['textvariable'] = self.scorestringvar
def removeLetters(self, s):
a = []
for i in s:
if i.isdigit():
a.append(i)
return ''.join(a)
def noLettersValidate(self, sv, w):
w.text(self.removeLetters(sv.get()))
Thanks for everyone contributions!

Related

What causes an exception in Tkinter callback? [duplicate]

This question already has an answer here:
TypeError: 'NoneType' object is not callable with tkinter
(1 answer)
Closed 1 year ago.
I'm trying to make a program that you can add infinite rooms to, so all of my code is built around using one variable to deduce which room is which. However when I run it, it gives me an error that doesn't directly reference any one line in my code, and since I'm a pretty new programmer, I don't know what it means. Also my code is pretty all over the place and incomplete. Thanks for any help!
The error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\SCA0023\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1892, in __call__
return self.func(*args)
TypeError: 'NoneType' object is not callable
The Code
from tkinter import *
root = Tk()
class Room:
def __init__(self, items):
self.objects = []
self.objects.append(items)
def list(self):
print(self.objects)
def addkitchenmenu(r): #add a new option menu attributed to a new room
globals()[f'kitchenvar_{r}'] = StringVar(root)
globals()[f'kitchenvar_{r}'].set("Add an appliance")
globals()[f'kitchenvar_{r}'].trace('w', applianceadd(r))
kitchenitems = ['Kettle', 'Toaster']
globals()[f'appliancelist_{r}'] = OptionMenu(root, globals()[f'kitchenvar_{r}'], *kitchenitems).pack()
addkitchen(r)
def applianceadd(r): #add a new room
globals()[f'kobjects_{r}'] = []
globals()[f'kobjects_{r}'].append(globals()[f'kitchenvar_{r}'].get())
items = globals()[f'kobjects_{r}']
globals()[f'kroom_{r}'] = Room(items)
globals()[f'kroom_{r}'].list()
def addkitchen(r): #add an appliance
globals()[f'addappliace{r}'] = Button(root, text='add appliance', command=lambda: applianceadd(r))
def newkitchencheck(): #find the next name for a room that isn't taken
varnotfound = True
a = 0
while varnotfound:
if f'kroom{a}' in globals():
a += 1
else:
r = a
varnotfound = False
addkitchenmenu(r)
addroombutton = Button(root, text="add kitchen", command=newkitchencheck)
addroombutton.pack()
root.mainloop()
You are passing result of applianceadd(r) (which is None) to .trace(). Change to .trace("w", lambda *_: applianceaddr(r)).

'str' object has no attribute 'get' in Tkinter callback

I'm trying to trace an OptionMenu widget's choice so that different choices run a different function. However, I keep running into the error below:
Exception in Tkinter callback
Traceback (most recent call last):
File "-", line 1705, in __call__
return self.func(*args)
File "-", line 149, in callback
self.bookingCanvas(optVar)
File "-", line 152, in bookingCanvas
perfDate = optVar.get()
AttributeError: 'str' object has no attribute 'get'
Here is the relevant code:
optVar = StringVar(self.root)
optVar.trace('r', callback=self.callback)
optVar.set("06/10/20") # default value
optMenu_Label = Label(self.root, text='Select a performance date:',bg='gray15', fg='yellow', relief=GROOVE)
optMenu_Label.pack(side=TOP, anchor=NW, padx=15, pady=15)
optMenu = OptionMenu(self.root, optVar, '04/10/20','5/10/20','6/10/20')
optMenu.pack(side=TOP,anchor=NW, padx=35, pady=3)
The callback of the .trace() above:
def callback(self, optVar, *args):
print("SOMETHING HAS CHANGED")
self.bookingCanvas(optVar)
The error area (I assume):
def bookingCanvas(self, optVar):
perfDate = optVar.get()
print("This is a Date >>>",perfDate)
print("did we get here?")
The callback from a trace is given the name of the variable, not a reference to the variable object. The name is a string, which is why you're getting the error AttributeError: 'str' object has no attribute 'get'.
With the name, you can use the common widget method getvar to get the value stored in the variable.
In your definition of callback you can get the value and pass it to your function like so: (assuming for this example that you have a global variable named 'root` which represents the root widget)
def callback(self, optVar_name, *args):
print("SOMETHING HAS CHANGED")
value = root.getvar(optVar_name)
self.bookingCanvas(value)
With that, you can remove the call to get in bookingCanvas:
def bookingCanvas(self, perfDate):
print("This is a Date >>>",perfDate)
print("did we get here?")

Unable to access a variable in a function which belongs to other function of same class?

I am new to OOPS.I am creating a GUI with tkinter in python.I am trying to access a variable in a function which belongs to other function in same class.Getting error as 'labels' object has no attribute 'bgcolor'.bgcolor belongs to lbl_property function and i am accessing it in write function.Getting error at line no:85
Traceback :
Traceback (most recent call last):
File "C:\Users\PC\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "E:\Colsoftware\Python1\Created Python\Tkinter IDE\Page.py", line 138, in write
self.obj.write(self.dict1)
File "E:\Colsoftware\Python1\Created Python\Tkinter IDE\label.py", line 85, in write
string='\n'+j+'='+'Label('+self.title+','+'bg='+self.bgcolor+','+'fg='+self.fgcolor+','+'width='+self.width+','+'height='+self.height+','+'anchor='+self.loc_var1.get()
AttributeError: 'labels' object has no attribute 'bgcolor
Program
from tkinter import *
from dnd import *
class labels:
def __init__(self,root,prop,title):
self.root=root
self.prop=prop
self.counter=1
self.title=title
#=========================
def fcounter(self):
self.counter+=1
#-----------------
def label(self,dict1):
self.name_var=Label(self.root,text='Label'+str(self.counter))
self.name_var.pack()
self.name_var.bind("<Button-2>",self.lbl_property)
make_draggable(self.name_var)
if 'label_widget' in dict1.keys():
dict1['label_widget'].append('label'+str(self.counter))
else:
dict1['label_widget']=['label'+str(self.counter)]
self.fcounter()
print(dict1)
def lbl_property(self,event):
#====================
self.lbl_variable=Label(self.prop,text="Variable",font=("",13,""),anchor="w",width=18,bg="white")
self.lbl_variable.place(x=10,y=10)
self.lbl_bgcolor=Label(self.prop,text="Background Color",font=("",13,""),anchor="w",width=18,bg="white")
self.lbl_bgcolor.place(x=10,y=50)
self.lbl_fgcolor=Label(self.prop,text="Text Color",font=("",13,""),width=18,anchor="w",bg="white")
self.lbl_fgcolor.place(x=10,y=90)
self.lbl_width=Label(self.prop,text="Width",font=("",13,""),anchor="w",width=18,bg="white")
self.lbl_width.place(x=10,y=130)
self.lbl_height=Label(self.prop,text="Height",font=("",13,""),anchor="w",width=18,bg="white")
self.lbl_height.place(x=10,y=170)
self.lbl_anchor=Label(self.prop,text="Anchor",font=("",13,""),anchor="w",width=18,bg="white")
self.lbl_anchor.place(x=10,y=210)
#======================
self.loc_var1=StringVar(self.prop)
self.loc_var1.set("center")
self.bgcolor="white"
self.fgcolor="black"
self.width=10
self.height=1
#==========================
self.ety_variable=Entry(self.prop,font=("",13,""), borderwidth=2, relief="groove")
self.ety_variable.place(x=190,y=10)
self.ety_variable.insert(0,self.name_var)
self.ety_bgcolor=Entry(self.prop,font=("",13,""), borderwidth=2, relief="groove")
self.ety_bgcolor.place(x=190,y=50)
self.ety_fgcolor=Entry(self.prop,font=("",13,""), borderwidth=2, relief="groove")
self.ety_fgcolor.place(x=190,y=90)
self.ety_width=Entry(self.prop,font=("",13,""), borderwidth=2, relief="groove")
self.ety_width.place(x=190,y=130)
self.ety_height=Entry(self.prop,font=("",13,""), borderwidth=2, relief="groove")
self.ety_height.place(x=190,y=170)
self.opt_anchor=OptionMenu(self.prop,self.loc_var1,"center","n","s","w","e","ne","nw","sw","se")
self.opt_anchor.config(width=24, relief="groove",bg="white",highlightthickness=0)
self.opt_anchor.place(x=190,y=210)
#===========
self.btn_apply=Button(self.prop,text="Apply",command=lambda:self.apply(event), borderwidth=2, relief="groove",width="20",bg="white")
self.btn_apply.place(x=150,y=650)
#=================
def apply(self,event):
if self.ety_bgcolor.get()!="":
self.bgcolor=self.ety_bgcolor.get()
if self.ety_fgcolor.get()!="":
self.fgcolor=self.ety_fgcolor.get()
if self.ety_width.get()!="":
self.width=self.ety_width.get()
if self.ety_height.get()!="":
self.height=self.ety_height.get()
if self.loc_var1.get()!="center":
self.loc_var1.set(self.loc_var1.get())
event.widget.configure(bg=self.bgcolor,fg=self.fgcolor,width=self.width,height=self.height,anchor=self.loc_var1.get())
def write(self,dict1):
fwrite=open('dummy.py','a')
for i in dict1.values():
for j in i:
self.wname=str(i).replace('.!toplevel.!','')
print(self.wname)
string='\n'+j+'='+'Label('+self.title+','+'bg='+self.bgcolor+','+'fg='+self.fgcolor+','+'width='+self.width+','+'height='+self.height+','+'anchor='+self.loc_var1.get()
fwrite.write(string)
Your class instance does not have a bgcolor attribute when first instantiated because there is no assignment in __init__.
>>> class Labels:
... def __init__(self,root,prop,title):
self.root=root
self.prop=prop
self.counter=1
self.title=title
>>> l = Labels(1,2,3)
>>> hasattr(l,'bgcolor')
False
>>>
The bgcolor attribute is defined in the lbl_property` method
>>> class Labels:
... def __init__(self,root,prop,title):
self.root=root
self.prop=prop
self.counter=1
self.title=title
def lbl_property(self,event):
self.bgcolor = 5
You need to call that method first
>>> l = Labels(1,2,3)
>>> l.lbl_property('foo')
>>> hasattr(l,'bgcolor')
True
>>>
Or add it to __init__
>>> class Labels:
... def __init__(self,root,prop,title):
self.root=root
self.prop=prop
self.counter=1
self.title=title
self.bgcolor=None
>>> l = Labels(1,2,3)
>>> hasattr(l,'bgcolor')
True
>>>

python NameError variable already set to global

I want to Change the Position of a spinbox and an entry field by clicking on a button.
Clicking on the button Triggers this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
File "GUI.py", line 79, in changesite
if (site==0):
NameError: global name 'site' is not defined
My code is this here:
else:
w = Spinbox(dialog, from_=5, to=getmodulo(ceiling), increment = 5.0, state='readonly',font = "bold")
e = Entry(dialog,font = "bold")
e.place(x=390,y=120)
w.place(x=20,y=120)
site = 0
def changesite():
global site
if (site==0):
e.destroy()
w.destroy()
ws = Spinbox(dialog, from_=5, to=getmodulo(ceiling), increment = 5.0, state='readonly',font = "bold")
es = Entry(dialog,font = "bold")
es.place(x=20,y=120)
ws.place(x=390,y=120)
site = 1
if (site ==1):
ws.destroy()
es.destroy()
w = Spinbox(dialog, from_=5, to=getmodulo(ceiling), increment = 5.0, state='readonly',font = "bold")
e = Entry(dialog,font = "bold")
e.place(x=390,y=120)
w.place(x=20,y=120)
site = 0
As you see I use global for site, so how can I fix this error? I don't know how to do at the Moment.
All the best;
This is sort of a wild guess, since you do not show us all of your code, but I could reproduce the problem when the "global" variable is not really global, but defined in an enclosing function.
Minimal example:
# global f is expected here...
def foo():
f = 42 # ... but defined here
def bar():
global f
f = f + 1 # this causes a NameError
bar()
foo()
Instead, you could make site a member of the class (if any), or even of the outer method (like name_of_outer_method.site), or make it truly global, or use a modifyable wrapper type.

Python function reference being passed into constructor turns into c_void_p datatype

To make a long story short, I'm trying to pass a list of dictionaries into a container class, with the intention that each dictionary will be used to instantiate another class. The problem is that each dictionary contains a function object reference to be assigned to the subclass, and for some reason just before the innermost subclass is instantiated, it changes from a python function object into a c_void_p object.
The application domain is the creation of a library of text-based UI widgets using curses.
Here is the 'child' class which the container is meant to contain:
class DigitalReadout(Window):
# Just a one-line borderless window displaying some data...
def __init__(self, width, y, x, label, digits, data_source, parent=None):
super(DigitalReadout, self).__init__(1, width, y, x, parent)
self.data_source = data_source
self.data = self.get_data_from_source()
self.label = label
self.digits = digits
self.spaces = self.width - len(self.label) - self.digits # Calc Number of extra spaces
###Irrelevant display-related classes omitted###
def update_data(self):
self.data = self.get_data_from_source() #return data from function
def get_data_from_source(self):
return self.data_source.__call__()
And here is the 'container' class:
class ReadoutPanel(BoxedWindow):
def __init__(self, y, x, readouts, parent=None):
super(ReadoutPanel,self).__init__(2 + len(readouts), self.find_longest_readout_width(readouts) + 2, y, x, parent)
self.children = []
self.initialize_readouts(readouts)
def find_longest_readout_width(self, readouts):
#Find the longest member and size container accordingly
longest_length = 0
for each_dict in readouts:
this_dict_length = each_dict['digits'] + len(each_dict['label']) + 1
if this_dict_length > longest_length:
longest_length = this_dict_length
return longest_length
def initialize_readouts(self, readouts):
y_readout_index = 1
for each_hash in readouts:
function = each_dict['func']
function()
self.children.append(DigitalReadout(each_dict['digits'] + len(each_dict['label']) + 1,
1,
y_readout_index,
1,
function,
self.window))
For reference, the base classes Window and BoxedWindow can be viewed here
When I run the following test code, I get the subsequent error:
if __name__ == '__main__':
#standard cuses initialization here...
from time import clock
i = 0
def print_i():
return str(i)
readouts = [{'label': 'count',
'digits': 10,
'func': print_i},
{'label': 'clock',
'digits':10,
'func': print_i}]
readout_panel = ReadoutPanel(1, 1, readouts) #Initialize that puppy!
curses.endwin()
Error:
Traceback (most recent call last):
File "window.py", line 515, in <module>
readout_panel = ReadoutPanel(1, 1, readouts)
File "window.py", line 455, in __init__
self.initialize_readouts(readouts)
File "window.py", line 476, in initialize_readouts
self.window))
File "window.py", line 183, in __init__
self.data = self.data_source()
TypeError: 'c_void_p' object is not callable
Printlining reveals that the function is being fetched from the dictionary and is still a function object. Once it gets passed into the constructor for DigitalReadout, however, it somehow comes back a c_void_p object. Any ideas why this is happening?
Thanks in advance, and apologies for the horrendously long question.
This is the constructor of DigitalReadout:
def __init__(self, width, y, x, label, digits, data_source, parent=None)
This is how you call it:
DigitalReadout(each_dict['digits'] + len(each_dict['label']) + 1, # width
1, # y
y_readout_index, # x
1, # label
function, # digits
self.window) # data_source
Looks like you're missing a parameter in the constructor (height?), because if I'm reading it right, the function should be data_source and it now is digits.

Categories