python Tkinter ListBox callback: passing a class-instance-specific callback - python

Here is the strange thing:
Say, you have a tk.Button (RUN) inside a class and you can specify a callback like
self.RUN["command"] = self.run
This refers to a function inside the class, which has access to all members.
In essence, the callback sees a class instance.
You can also create a tk.ListBox. The machinery of getting the selection involves events.
You cannot pass an instance-of-a-class-function as above.
The callback sees only the opaque event/widget things in Tk.
You can do
def CurSelect(evt) :
...
lb.bind('<<ListboxSelect>>',CurSelect)
but there is no way to pass more than the event.
So, when you have several ListBoxes running in the mainloop, what happens?
How can you map event/widget to the class instance that owns the event/widget?
Sure, you could make up some global maps. (Ugly, tried, I hate it.)
Anything better out there?

You can try to use lambda to pass arguments.
arguments = []
widget.bind("<>", lambda event, arg=arguments: cur_select(event, arg))
def cur_select(event, arg):
return
Also, naming convention for Python function: lowercase with words separated by underscores as necessary to improve readability.

Related

How to correctly create an instance of a class with exec() function?

I'm making a GUI using the TKinter library from Python. I want the user to select an option from a Combobox and then, to press a Button, which should create an instance of a class named as the selected option. In order to save code, I decided to use the exec() fuction in this way:
exec('instance = ' + comboExample.get() + '()').
This starts the __init__() method of the class, but when I try to call an other method (in this case from an inherited class) using instance.method() it displays the following error: NameError: name 'instance' is not defined. Here you have an example of the script:
from tkinter import *
from tkinter import ttk
master = Tk()
#Create classes
class Base():
def method(self):
self.label = Label(master, text = self.sentence)
self.label.pack()
class Example1(Base):
def __init__(self):
print('Example1 created')
self.sentence = 'This is example 1.'
class Example2(Base):
def __init__(self):
print('Example2 created')
self.sentence = 'This is example 2'
#Create Combobox and Button
combo = ttk.Combobox(master, state = 'readonly')
combo['values'] = ['Example1', 'Example2']
combo.pack()
def callback():
exec('instance = ' + combo.get() + '()')
#Here is the error
instance.method()
button = Button(master, command = callback, text = 'Button')
button.pack()
master.mainloop()
I don't now why but when I try with the following code it works properly:
class Example():
def __init__(self):
self.text = 'This is an example'
def add_text(self):
print(self.text)
exec('instance = Example()')
instance.add_text()
At the moment, I've only found one solution, which consists in not using exec(), but makes me waste more code than using it, especially if I want to create a lot of classes like Example1 and Example2. It's all like the previous big script, but changing the callback() function:
def callback():
if combo.get() == 'Example1':
instance = Example1()
if combo.get() == 'Example2':
instance = Example2()
instance.method()
That's all. I started programming in Python only 2 months ago and I'm also new in stackoverflow, so if I've made some mistake in the explanation or anything, please tell me and I'll fix it.
Thanks for your time. Any help would be appreciated.
The issue isn’t your syntax; it’s that you’re trying to do something illegal. You can’t create new local variables with exec. (The reason the same code outside a function works is that in general you can create a new global variable with exec, but it’s still a bad idea.)
But you also don’t need to do that. In Python, everything is an object, including classes. So, you just need the get the class from the name. Then you can create an instance of that class, and store it in a local variable, by just using the same normal syntax you’d use for instantiating a class statically and storing it in a local variable.
The right way to do this is to store a dictionary mapping names to class objects. If you want to get clever, you can write a decorator that registers classes with that dictionary, but if that sounds like Greek to you, just do it explicitly:
classes = {'Spam': Spam, 'Eggs': Eggs}
If you have dozens of these, you can avoid the repetition with a comprehension like this:
from your_module import Spam, Eggs
classes = {cls.__name__: cls for cls in (Spam, Eggs)}
… but at that point you’re probably better off learning how to write the decorator.
Either way, you can fill your combo box with the keys of that dictionary instead of repeating yourself in the combo['values'] line.
And then, to create an instance, you just do this:
cls = classes[comboExample.get()]
instance = cls()
(Obviously you can collapse that into a single line, but I thought it would be easier to understand if we keep the two parts separate.)
If you really want to do this in a hacky way, you can. Every class that you’ve created in this module is already stored in a dictionary by name—the module’s global namespace. That’s the same place you were trying to find it implicitly with exec, but you can find it explicitly by just looking it up in globals(). However, the global namespace also has the names of all of your functions, imported modules, top-level constants and variables, etc., so this is usually a bad idea. (Obviously, exec has the exact same problems.)
You should not be using exec for this purpose. exec is a powerful tool, but it's the wrong tool for this job.
A much simpler approach is to create a mapping from user inputs to classes. You can then use that mapping both for the combobox and for the callback.
Example:
...
mapping = {"Example1": Example1, "Example2": Example2}
#Create Combobox and Button
combo = ttk.Combobox(master, state = 'readonly')
combo['values'] = sorted(mapping.keys())
combo.pack()
def callback():
class_name = combo.get()
cls = mapping[class_name]
instance = cls()
instance.method()
...
You could even automatically generate the mapping by iterating over a list of classes, though for this example that seems like overkill.

How do you decide which level a function should be at in python?

I have a file called file_parsers.py and it contains the following class:
class FileParser():
def __init__(self, file_text):
self.file_text = file_text
def do_something(self):
my_value = func_with_no_state()
I'm not sure what questions to ask when deciding whether func_with_no_state() should be inside the class or outside of the class as a file-level function?
Also, is it easier to stub this function when it is at a file-level or inside the class?
So... Does any other class use func_with_no_state? If not, it should be hidden within FileParser. If something else does use it, you have a bigger question. If OtherClass uses func_with_no_state pretty frequently (on par with FileParser) then it would be a good idea to keep func_with_no_state outside so that both classes can use it. But if FileParser is by far the main user, then OtherClass could just pull the function from FileParser's definition.

traits api creating new trait and trait changed behavior

I often find myself in this situation:
class A:...
B=class
a=Instance(A,())
#on_trait_change('a')##I would really like to be able to do this
def do_something(...)
I think that this currently triggers if you were to reset the entirety of the class. e.g. b=B(). b.a=A() should trigger it. But I would like to control when my custom class signals that it has been 'changed'. Per haps one might like A to signal 'changed' if merely a member of A is changed e.g. b.a.x+=1
If both A and B derive from HasTraits, then changing your decorator to #on_trait_change('a.+') will do what you want. If you change the signature of your do_something to two or more arguments, you'll even be able to detect which attributes of a changed. (See http://traits.readthedocs.org/en/latest/traits_user_manual/notification.html#notification-handler-signatures.)

Pythonic approach to defining __init__ helper methods?

Please excuse me, I'm new to Python and trying to learn the Pythonic approach. I'm designing a class that essentially initializes its state from a number of different sources (files). I've isolated this behavior to a single instance method, _load_from_file(file). It's called a number of times in __init__, but I typically like to keep my constructors at the beginning of a class definition, and my internal helper methods towards the end.
However, if I were to take this approach, _load_from_file isn't defined at the point in __init__ where I'd like to use it. How do you pythonistas lay this situation out?
To elaborate:
class Thing(object):
def __init__(self, file_path):
f = open('file_path')
_load_from_file(self,"someData",f) # ISSUES!
_load_from_file(self,"moreData",f) # WRONG!
f.close()
# Interface
# ...
# Internal - Where do you guys put this stuff?
def _load_from_file(self,thingToLoad,file):
# logic
Are you sure it won't work in the order you're already using? Remember, you're not using C. The called method doesn't have to appear in the class definition before calling code, so long as it has been defined by the time it gets called.
I would, however, change this:
_load_from_file(self)
to this:
self._load_from_file()
Any name-not-defined error you were getting was not because your method call was at a file position earlier than the method's definition, but because you tried to call it like a global function instead of via an object on which the method is defined.

Give a class its own `self` at instantiation time?

I've got a button class that you can instantiate like so:
engine.createElement((0, 0), Button(code=print, args=("Stuff!",)))
And when it is clicked it will print "Stuff!". However, I need the button to destroy itself whenever it is clicked. Something like this:
engine.createElement((0, 0), Button(code=engine.killElement, args=(self,)))
However, that would just kill the caller, because self refers to the caller at that moment. What I need to do is give the class its own 'self' in advance...
I thought of just making the string 'self' refer to the self variable upon click, but what if I wanted to use the string 'self' in the arguments?
What is the way to do this? Is my architecture all wrong or something?
Unfortunately this is impossible — the arguments to the button's constructor are evaluated before the constructor is evaluated. You'd need to assign the button to a variable, then set the callback afterwards:
b = Button(code=engine.killElement)
b.args = (b, )
Or something similar.
You've essentially set it up so that you need a reference to an object in order to create that object, which is of course impossible. You could do something like this (a list is as good as a tuple for argument unpacking):
arglist = []
button = Button(code=engine.killElement, args=arglist)
arglist.append(button)
engine.createElement((0, 0), button)
This is inelegant, unclear, and verbose, but it'll get the reference to the instance into the instance.
You could use a sentinel value as another poster suggested. Perhaps a better suggestion would be to simply use a convention (like Python itself) that self is always passed as the first argument to the specified function and doesn't need to be specified explicitly. Then your callbacks are written to always take self, even if they don't do anything with it.
But generally you would not specify an object's behavior by passing it to that object's constructor, but through inheritance. In other words you'd subclass Button, override its onClick method or whatever, and instantiate the subclass. Having onClick know what instance it's attached to is a non-issue. So I come down on the side of yes, your architecture is a wee bit all wrong.
This is impossible in general.
However, if you're creating the Button class, you can pass a special sentinel value that means "yourself". For example:
class Button(object):
yourself = 'yourself'
def __init__(self, code, args):
self.code = code
self.args = [self if arg is yourself else arg for arg in args]
Then:
engine.createElement((0, 0), Button(code=engine.killElement, args=(Button.yourself,)))
Picking an appropriate sentinel can be tricky—obvious choices like None, 0, or '' may be legitimate values, and even tricky things you come up with may turn out to be useful arguments during debugging. Making yourself a class variable, or a global within the module, means that if you ever do need to redefine the sentinel, you only need to change it in one place, instead of everywhere you use it.
See http://bytes.com/topic/python/answers/555169-sentinel-values-special-cases for a brief discussion on picking an appropriate sentinel value. There's another blog out there with more information, but I haven't found it in a quick search… Anyway, here are some quick ideas:
None is always the best answer if it works.
Define an empty class to be the sentinel. Either the class object, or any instance of the class object, can be used.
Create a global instance of the object class (object()).
Define an empty function and use it (or its func_code or whatever).
Ellipsis (or type(Ellipsis), which is a type named ellipsis, but that name isn't accessible) is almost always safe, because it's only used in __getitem__ and friends (and possibly in defining slice objects to pass to them).
If there's a type that could not possibly be a valid value, and you've already got instances around, use one of those—e.g., the func_code member of the __init__ function.
Maybe something like this would help:
class Button(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
kwargs['args'].append(obj)
return obj
def __init__(self, code, args):
self.code = code
self.args = args
def click(self):
return self.code, self.args
b = Button(code="engine.killElement", args=[])
print b.click()
Output:
('engine.killElement', [<__main__.Button object at 0x00B59AF0>])

Categories