Typical way of creating multiple tkinter entries/buttons/ - python

I have a question regarding Tkinter in Python.
I was thinking of the smartest/most commonly used way of creating GUIs with many/multiple Entries, Lables, Buttons, etc.
In my first sketches I just put them before the window.mainloop(), which at some point becomes confusing and unreadable. I then used methods for each Label, Button,... like this:
def someInput():
someInput = Entry(controlFrame)
someInput.grid(column = 1, row = 1)
...
Now I was thinking, if it makes sense to put them into a class like this:
class allInputs():
def inputOne(*args):
...
def inputTwo(*args):
...
What is your preferred way of doing this and why?
Thanks in advance.

My typical framework will look something like this
class App(tk.Frame):
def __init__(self,master=None,**kw):
tk.Frame.__init__(self,master=master,**kw)
self.btnOne = tk.Button(self,text="Hello")
self.btnOne.grid()
self.ow = OtherWidget(self)
class OtherWidget(tk.Frame):
....
if __name__ == '__main__':
root = tk.Tk()
App(root).grid()
root.mainloop()
The main application is a class (a sub-class of a tk Frame) and each "sub-component grouping" of widgets is its own class.
For example if I have a button which populates a text box, then they will be part of the same class. A textbox and it's scrollbar will be another class.
Basically I will group together widgets by their function and put them in the same class, all of which will be grouped in to the application class.
This makes sense to me as any data shared between widgets exist within the same class 'namespace' and they can access each others data without having to deal with globals.

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.

Keeping classes loosely coupled and sharing data

I've been working in python on a project where I have a GUI which I split up a bunch of the work between classes. I don't know a lot of the best practices for passing data around between classes, and I've frequently run into the issue, where I have to implement something, or change something for work, and I've resorted to making a lot of the classes objects of another class in order to give it the data I need.
Any ideas or suggests would be greatly appreciated on how to keep my classes independent for later modification and still pass the relevant data around without affecting interfaces too much?
As an example
class Window():
def __init__(self, parent=None):
self.parent = parent
def doStuff(self):
#do work here
class ParseMyWork(Window):
def __init__(self, parent=None):
self.parent=parent
I often find myself doing stuff like the above giving objects to class Window
or simply inheriting everything from them as in ParseMyWork
There must be better and cleaner ways of passing data around without making my classes utterly dependent on eachother, where one little change creates a cascade effect that forces me to make changes in a bunch of other classes.
Any answers to the question don't necessarily have to be in python, but it will be helpful if they are
If I'm understanding your question correctly, I would say that inheritance is not necessary in your case. Why not give ParseMyWork a function for dealing with a specific Window task?
class Window():
def __init__(self, parent=None):
self.parent = parent
def doStuff(self):
#do work here
class ParseMyWork():
def __init__(self, parent=None):
self.parent=parent`
def doWindowActivity(self, window):
window.doStuff
Then you can use the function like this
work_parser = ParseMyWork()
window = Window()
work_parser.doWindowActivity(window);
That way you can use your work_parse instance with any window instance.
Apologies in advance for my Python, it's been a while so if you see any rookie mistakes, do point them out.
Keep it simple.py:
def doStuff(window):
#do work here
return window
def parseStuff(stuff):
pass
really.py:
from simple import doStuff, parseStuff
def really_simple(window):
okay = doStuff(window)
return parseStuff(okay)
don't complicate the class:
from really import really_simple
really_simple(window)
imo: classes are overly complicated objects, and in a lot of cases more confusing than they need to be, plus they hold references and modify stuff, and can be difficult to decouple once they have been tied to other classes. if there isn't a clear reason why a class needs to be used, then it probably doesn't need to be used.
Classes are super powerful, so it's good you're getting started with em.
Discalimer: Haven't worked in python for a while now, so things might not be exact. The general idea still applies though.
Getting into your question now:
I would say the best way to achieve what you want is to create an instance of the first object where you will extract information from.
Now when creating a class, it's vital that you have attributes within them that you will want to be stored within it that you would like to retrieve once the class is instantiated.
For example, using your Window class example above, let's say that you have an attribute called resolution. It would look something like this:
class Window():
def __init__(self, parent = None):
self.parent = None
self.resolution = '40x80'
Now the resolution information associated with your Window class is forever part of any Window class instance. Now, the next step would be to create a get method for resolution. This should be done as follow:
class Window():
def __init__(self, parent = None):
self.parent = None
self.resolution = '40x80'
def getResoultion():
return self.resolution
Now, the reason we created this get method is because we can now set a variable to the information that is returned with it.
So let's say that you have everything associated with your Window class in its own file (let's say the file name is called Window.py). In a separate file (let's call it main.py), you can do the following:
import Window
windowInstance = Window()
windowResolution = windowInstance.getResolution()
If you print out the variable windowResolution, you should get that 40x80 printed out.
Now, as a side note, I do believe it is possible to get the information associated with an attribute with an instance of a class by simply doing something like
windowResolution = windowInstance.resolution
but that is bad practice in general. The reason, in a nutshell, is because you are now exposing attribute names of your class which you do not want to do because it makes it easy for a person outside of your code to learn the name where that information is held and change it. This can then lead to a myriad of other problems when it comes to making an overall program work. That is why it is best practice to use getters and setters. I already showed what getters are. Simply a get method for attributes. Setters, as you can probably assume, allow for one to set the information of an attribute to something else. Now you might say "Gabe, if we can create setter methods, what's the point of it if they just change it". My answer to that is to not give a setter method to all attributes. For attributes you don't mind for a person to change, give it a setter method, but for attributes you do not want any outside users to touch, simply don't create a setter method for it. Same goes with getter methods too. Users don't need to see all of the information of all attributes that makes your program work. Here's a better explanation: https://en.wikipedia.org/wiki/Mutator_method
Now, back to your example. Now let's say you have your ParseMyWork class in its own file like we did with your Window class, and let's say that ParseMyWork needs the resolution info from Window class. You can do the following :
import Window
import ParseMyWork
windowInstance = Window()
windowResolution = windowInstance.getResolution()
parseInstance = ParseMyWork(windowResolution)
This will only pass the window resolution information associated with your Window class. Hope this helps.

Python 3 and Tkinter. Too many global variables

I started programming a simple GUI application in Python, using Tkinter library.
Everything works fine but, to dynamically change any widget (for example a button from "login" to "logout", etc), I have to get the variable containing the widget object itself in the function from outside.
I can do this in two ways:
passing the widget as a argument;
using global variables.
A simplified example:
1) Passing as argument:
def changeLabel(b):
b.config(text='Logout')
btn1 = Button(f0,text="Login",width=15)
btn1.grid(row=1,column=2,rowspan=1,padx=10,ipadx=10)
changeLabel(btn1)
2) Or using a global variable:
def changeLabel():
global btn1
btn1.config(text='Logout')
btn1 = Button(f0,text="Login",width=15)
btn1.grid(row=1,column=2,rowspan=1,padx=10,ipadx=10)
changeLabel(btn1)
Now, I know that global variables should be avoided but passing every widget as argument through many functions, even in a simple application, it's a mess.
So, what's the best way to manipulate the Tkinter widgets at runtime? Can you suggest me the right way?
Thank you
The best way to manipulate tkinter widgets at runtime is to store those widgets as attributes of a class.
For example:
class Example(...):
def create_widgets(self):
...
self.btn1 = Button(...)
...
def changeLabel(self):
self.bt1.config(...)
For more information see Best way to structure a tkinter application

python / tk - affecting widget in other class

I am attempting to create a ttk.notebook in python where the selection in one tab affects the selection of a widget in a separate tab. Each tab is currently set up as a different class. Is there a way to pass or call a function in one class(tab) and have it change the widget/call a function in the other class(tab)?
in short, i have two functions: lb1 and lb2 (for tk lisboxes). Ideally, I would like the selection function on lb1 to call a function to populate a list in lb2. Each are in different classes.
a general sample of what I am trying to do follows.
class One(ttk.Frame):
...
lb1 = Listbox(listvariable = apps, height = 5)
def lb2_lookup(self, *args):
#this would somehow call a function to populate lb2
self.lb1.bind('<<ListboxSelect>>', self.lb2_lookup)
class Two(ttk.Frame):
lb2 = Listbox(listvariable = lb2apps, height = 5)
Thanks a bunch. I apologize if the code sample makes no sense, but I believe it gets the general point across.
The solution is that for one class to call a function in another, it simply needs to have a reference to that class, or a reference to some sort of controller class that has a reference to the other class.
class One(ttk.Frame):
def __init__(self, master, other_class):
self.other_class = other_class
...
self.lbl.bind('<<ListboxSelect>>`, self.other_class.lb2_lookup)
two = Two(...)
one = One(..., other_class = two)
Another way to accomplish the same thing is to have the class provide an interface, so that you can connect the classes after they are created:
class One(...):
...
def set_target(self, other_class):
self.other_class = other_class
class Two(...):
...
one = One(...)
two = Two(...)
one.set_target(two)
Finally, as written your code is tighly coupled. That means that even a small change in Two might mean you have to modify class One as well. That makes for code that is hard to maintain. You should create an interface that doesn't require one class to know much about the implementation of the other class.
Specifically, in your example you are setting up a binding to call lb2_lookup. But what if you change class Two and rename lb2 to lb3? Do you really want to have to also modify One? Better to create a function in Two that doesn't directly relate to a widget. For example:
class One(...):
...
self.lb1.bind('<<ListboxSelect>>', self.other_class.lookup)
With that, you are now free to reimplement Two however you want. The only requirement is that you keep a method named lookup. However, exactly what lookup does can change as long as it works the same way.
So, for example, right now lookup could return the value from a widget named lb2, but later it could look up data from a widget named foobar. No matter what lookup does, as long as it works in the same way (takes the same arguments, returns the same type of result), you won't have to modify One whenever you change Two.

which one of those python implementations is better

which one of the following is considered better a design and why ?.
i have 2 classes , one for the gui components and the other is for it's events.
please put in mind that the eventClass will be implemented so many times, (sometimes to get data from an oracle databases and sometimes mysql databases )
class MainWindow:
def __init__(self):
self.myEvents = eventClass() # the class that has all the events
self.button = button # consider it a button from any gui library
self.menu = menu # menu box
def bottonEvent(self):
data = self.myEvents.buttonEvent()
self.menu.populate(data)
class eventClass:
def __init__(self):
pass
def getData(self):
return data # return data to puplate in the list
OR
class MainWindow:
def __init__(self):
self.myEvents = eventClass(self) # the class that has all the events
self.button = button # consider it a button from any gui library
self.menu = menu # menu box
def bottonEvent(self):
self.myEvents.ButtonEvent()
class eventClass:
def __init__(self,window):
pass
def ButtonEvent(self):
window.menu.populateData()
please inform me if anything was unclear
please help ,
thanks in advance
The first choice is better "decoupled": the event class needs and has no knowledge whatsoever about the window object or its menu attribute -- an excellent approach that makes the event class especially easy to unit-test in isolation without any overhead. This is especially nice if many implementations of the same interface need to exist, as you mention they do in your case.
The second choice introduces a mutual dependency -- an event object can't work without a window object, and a window object builds an event object. That may be an acceptable complication in more abstruse cases where it buys you something, but for this specific use it sounds more like an arbitrary extra difficulty without any real plus.
So, I would recommend the first form.

Categories