Related
As the question states, I can't seem to fully grasp the point of using classes with tkinter.
I have read through a decent number of different sites but I keep getting search results on how to create and use classes, but none so far have been able to get through to me. I've even scoured through the suggested questions while asking this one. The closest I've come to understanding is Bryan's explanation on this answer to the question Why use classes when programming a tkinter gui?
But still, I feel like I'm almost there, just not quite over the edge of understanding.
In his example in the link, he creates an unconventional program, and then a better, more conventional program that does the same thing. I know that it represents a much smaller scale than the thousand-line programs that could really benefit from an object oriented approach.
Does every widget need to be in its own separate frame that's maybe part of an even bigger frame?
Can classes have methods that create and place a frame? In addition, can those same classes have methods than can create, modify, and place a widget within the previously made frame?
I also have some code that allows me to create, modify, and place a widget. Although I know it's not conventional, so I would greatly appreciate some input on this as well. Any suggestions on what you would do with this code to make it better?
import tkinter as tk
def layout(self, row=0, column=0, columnspan=None, row_weight=None, column_weight=None, color=None, sticky=None, ipadx=None, padx=None, ipady=None, pady=None):
self.grid(row=row, column=column, columnspan=columnspan, sticky=sticky, ipadx=ipadx, padx=padx, ipady=ipady, pady=pady)
self.grid_rowconfigure(row, weight=row_weight)
self.grid_columnconfigure(column, weight=column_weight)
self.config(bg=color)
class MyLabels(tk.Label):
def __init__(self, parent, text, **kwargs):
tk.Label.__init__(self, parent, text=text)
layout(self, **kwargs)
class MyButtons(tk.Button):
def __init__(self, parent, text, command, **kwargs):
tk.Button.__init__(self, parent, text=text, command=command)
layout(self, **kwargs)
window = tk.Tk()
test_button = MyButtons(window, "hi", None, color="pink")
window.mainloop()
Edited after comments:
So I've been working many hours since yesterday trying to incorporate the ideas you've had for me. This is what I came up with:
import tkinter as tk
window = tk.Tk()
class MyWidgets(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.layout()
def layout(self):
self.grid(row=0, column=0)
self.config(bg="blue")
class MyButtons(MyWidgets):
def __init__(self, parent, text):
MyWidgets.__init__(self, parent)
tk.Button(parent, text=text)
self.layout()
frme = MyWidgets(window)
btn = MyButtons(frme, text="Test")
window.mainloop()
I've tried moving things around and rewriting lots of areas on this little side program, and even though I was able to prove that btn is infact accessing the self.config(bg="blue") attribute, the button doesn't appear to change. As a matter of fact I can't find a way to make the button appear without needing to put self.grid() in the child class just after the button is created.
Still, even if I did add the self.grid() the button still won't turn blue. Is it something with self?
Why won't the button appear when the child class creates it, and the parent class places it?
Note: I've purposefully omitted the entire layout function and replaced it with just a simple config method. I figure if I can understand this, I can then find a way to incorporate the whole function back into the code.
Does every widget need to be in its own separate frame that's maybe part of an even bigger frame?
That's a bit like asking if every part of a mathematical expression needs parenthesis. Strictly speaking, the answer is "no". However, using frames to organize groups of widgets is a tool designed to make writing and understanding the code easier, much like parenthesis in a complex math equation makes writing and understanding the equation easier.
Can classes have methods that create and place a frame? In addition, can those same classes have methods than can create, modify, and place a widget within the previously made frame?
Yes, and yes. Methods in a class don't have any limitations on what they can and cannot do. Methods of a class can do anything that a normal function can do.
I also have some code that allows me to create, modify, and place a widget. Although I know it's not conventional, so I would greatly appreciate some input on this as well. Any suggestions on what you would do with this code to make it better?
"Better" is highly subjective. What is better for 20 lines of code might not be better for 200, 2,000, or 20,000. What is better for a function used exactly twice might not be better for a function used hundreds or thousands of times (or visa versa).
That being said, you're doing one thing that is very unconventional and which leads to making your code harder to understand: you're using self as a parameter for a function that is not a method of a class. self means something very specific to python programmers; using it outside of the context of a method is very confusing.
You should do one of two things for the method layout:
Rename self to be widget or any other term other than self
Create a base class that defines layout, and then have your classes inherit from the base class. In that case, self is the proper first argument.
This part of the answer refers to code which was added after I wrote my original answer.
The base class I was referring to needs to be a separate class. For example:
class Base():
def layout(self):
self.grid(row=0, column=0)
self.config(bg="blue")
class MyLabels(Base, tk.Label):
def __init__(self, parent, text, **kwargs):
tk.Label.__init__(self, parent, text=text)
self.layout(self, **kwargs)
class MyButtons(Base, tk.Button):
def __init__(self, parent, text, command, **kwargs):
tk.Button.__init__(self, parent, text=text, command=command)
self.layout(self, **kwargs)
This type of class is sometimes called a mixin because it's not designed to be instantiated as a standalone object. Rather, it "mixes in" some additional behavior to other classes. A mixin will typically have methods, but won't have its own __init__.
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.
I am new in Python classes and trying to write an interface for a scientific code via Tkinter. But I can not call a function (which is in another class and opens a different frame) from a class or function. I have been searching for more than 2 days but could not find an answer to my case. If you explain like explaining a child I would be very happy since I don't know much technical details.
My code structure is like:
class first_class(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
....
def WhateverFunction():
"do sth"
class second_class(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
.....
**I want to use "WhateverFunction" here**
.....
so basically, I can not access that function from another class.
The searches I found online like Python using methods from other classes method. Bu This did not solve my problem. It is maybe because I am working with different Tkinter frames. I do not now... Thanks already, cheers!
In your code you defined the function WhateverFunction as a local function within __init__. So it can't be seen from other sections of your code and it's impossible to be called.
Instead you can implement your function as a method, for example. It would look like this:
class first_class(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
....
def WhateverFunction(self):
"do sth"
Now the function can be called everywhere as a method of an instance of first_class:
first_class_instance = first_class()
first_class_instance.WhateverFunction()
I asked the exact same question on here a while back with no response. Basically, you can't do it. WhateverFunction only exists within the scope of __init__, which itself can only be called from second_class. This is done with the method described in the 'using methods from other classes' question you linked to, but it is useless for doing what you want to do because you may only get an output from __init__, and hence you can't access any functions defined within that function.
To work around this, why not define WhateverFunction globally and then call it as usual, or declare it outside of the __init__ function but still within first_class?
Just create an instance of that class, and call the method if the second_class instance should be composed of the first one.
def second_class(tk.Tk):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
first_object = first_class().WhateverFunction()
Now a better approach would be to inherit second_class from the first one instead, and calling the method considering both classes have same parent, provided inheritance makes sense.
def second_class(first_class):
def __init__(self, parent, controller):
super(second_class, self).__init__(parent, controller)
self.WhateverFunction()
Note:- please try to follow certain conventions of Python like naming classes with camel case, and methods/functions with snake case.
I am trying change a Label in a Class with a function.
class Fenster2(tk.Frame):
def __init__(self, parent,controller):
tk.Frame.__init__(self,parent)
label_fluss1 = tk.Label(self, width=9)
label_fluss1.grid(row=3,column=2)
label_fluss2 = tk.Label(self, width=9)
label_fluss2.grid(row=4,column=2)
GPIO.add_event_detect(27,GPIO.RISING,callback=hochzaehlen1)
GPIO.add_event_detect(22,GPIO.RISING,callback=hochzaehlen2)
GPIO.add_event_detect(23,GPIO.FALLING,callback=interrupt)
def hochzaehlen1(callback):
global ticks1
ticks1 +=1
return ticks1
def hochzaehlen2(callback):
global ticks2
ticks2 +=1
return ticks2
def interrupt(callback):
global ticks1
global ticks2
global fluss1
fluss1=ticks1/582.0 # liter/min
fluss2=ticks2/354.0 # liter/min
ticks1=0
ticks2=0
Fenster2.label_fluss1.config(text=str(fluss1))
pb_fluss1.config(value=fluss1)
label_fluss2.config(text=str(fluss2))
pb_fluss2.config(value=fluss2)
Now, when interrupt tries to configure label_fluss1 it says Fenster2 has no instance label_fluss1. Anyone knows how to make this work?
I know that I will get the same problem with label_fluss2 and both progressbars.
Any advice is much appreciated.
Greetings Sebastian
Believe you need to use self on those variables when creating a class. That could be the issue causing this. so in your init() you would have:
self.label_fluss1 = tk.Label()
You would do this for all the variables in init being defined and your methods.
As for the class itself goes, why are you making all your methods children of callback that doesnt exist?
I would read up on creating classes more on https://docs.python.org/3/tutorial/classes.html to get a better understanding of instance variables, global, and local. The scope in which these reside are very important for being used. If the class is created correctly, there is no reason that when you create the object that it should not have the fluss1 attribute. As it stands now though, you need to define self before these variables. Then go from there structuring your class properly to call these attributes as needed.
You are missing a basic understanding of how classes work. You need to make the variable you want to update an instance variable, and then call it the same way:
class Fenster2(tk.Frame):
def __init__(self, parent,controller):
tk.Frame.__init__(self,parent)
self.label_fluss1 = tk.Label(self, width=9)
self.label_fluss1.grid(row=3,column=2)
# etc...
def interrupt(self):
#etc ...
self.label_fluss1.config(text=str(fluss1))
I recommend you find a basic tutorial on classes before you continue, since GUIs rely heavily on them.
I'm trying to create a character generation wizard for a game. In one class I calculate the attributes of the character. In a different class, I'm displaying to the user which specialties are available based on the attributes of the character. However, I can't remember how to pass variables between different classes.
Here is an example of what I have:
class BasicInfoPage(wx.wizard.WizardPageSimple):
def __init__(self, parent, title):
wiz.WizardPageSimple.__init__(self, parent)
self.next = self.prev = None
self.sizer = makePageTitle(self, title)
<---snip--->
self.intelligence = self.genAttribs()
class MOS(wx.wizard.WizardPageSimple):
def __init__(self, parent, title):
wiz.WizardPageSimple.__init__(self, parent)
self.next = self.prev = None
self.sizer = makePageTitle(self, title)
def eligibleMOS(self, event):
if self.intelligence >= 12:
self.MOS_list.append("Analyst")
The problem is that I can't figure out how to use the "intelligence" variable from the BasicInfoPage class to the MOS class. I've tried several different things from around the Internet but nothing seems to work. What am I missing?
Edit I realized after I posted this that I didn't explain it that well. I'm trying to create a computer version of the Twilight 2000 RPG from the 1980s.
I'm using wxPython to create a wizard; the parent class of my classes is the Wizard from wxPython. That wizard will walk a user through the creation of a character, so the Basic Information page (class BasicInfoPage) lets the user give the character's name and "roll" for the character's attributes. That's where the "self.intelligence" comes from.
I'm trying to use the attributes created her for a page further on in the wizard, where the user selects the speciality of the character. The specialities that are available depend on the attributes the character has, e.g. if the intelligence is high enough, the character can be an Intel Anaylst.
It's been several years since I've programmed, especially with OOP ideas. That's why I'm confused on how to create what's essentially a global variable with classes and methods.
You may have "Class" and "Instance" confused. It's not clear from your example, so I'll presume that you're using a lot of class definitions and don't have appropriate object instances of those classes.
Classes don't really have usable attribute values. A class is just a common set of definitions for a collection of objects. You should think of of classes as definitions, not actual things.
Instances of classes, "objects", are actual things that have actual attribute values and execute method functions.
You don't pass variables among classes. You pass variables among instances. As a practical matter only instance variables matter. [Yes, there are class variables, but they're a fairly specialized and often confusing thing, best avoided.]
When you create an object (an instance of a class)
b= BasicInfoPage(...)
Then b.intelligence is the value of intelligence for the b instance of BasicInfoPage.
A really common thing is
class MOS( wx.wizard.PageSimple ):
def __init__( self, parent, title, basicInfoPage ):
<snip>
self.basicInfo= basicInfoPage
Now, within MOS methods, you can say self.basicInfo.intelligence because MOS has an object that's a BasicInfoPage available to it.
When you build MOS, you provide it with the instance of BasicInfoPage that it's supposed to use.
someBasicInfoPage= BasicInfoPage( ... )
m= MOS( ..., someBasicInfoPage )
Now, the object m can examine someBasicInfoPage.intelligence
Each page of a Wizard -- by itself -- shouldn't actually be the container for the information you're gathering.
Read up on the Model-View-Control design pattern. Your pages have the View and Control parts of the design. They aren't the data model, however.
You'll be happier if you have a separate object that is "built" by the pages. Each page will set some attributes of that underlying model object. Then, the pages are independent of each other, since the pages all get and set values of this underlying model object.
Since you're building a character, you'd have some class like this
class Character( object ):
def __init__( self ):
self.intelligence= 10
<default values for all attributes.>
Then your various Wizard instances just need to be given the underlying Character object as a place to put and get values.
My problem was indeed the confusion of classes vs. instances. I was trying to do everything via classes without ever creating an actual instance. Plus, I was forcing the "BasicInfoPage" class to do too much work.
Ultimately, I created a new class (BaseAttribs) to hold all the variables I need. I then created in instance of that class when I run the wizard and pass that instance as an argument to the classes that need it, as shown below:
#---Run the wizard
if __name__ == "__main__":
app = wx.PySimpleApp()
wizard = wiz.Wizard(None, -1, "TW2K Character Creation")
attribs = BaseAttribs
#---Create each page
page1 = IntroPage(wizard, "Introduction")
page2 = BasicInfoPage(wizard, "Basic Info", attribs)
page3 = Ethnicity(wizard, "Ethnicity")
page4 = MOS(wizard, "Military Occupational Specialty", attribs)
I then used the information S.Lott provided and created individual instances (if that's what it's called) within each class; each class is accessing the same variables though.
Everything works, as far as I can tell. Thanks.
All you need is a reference. It's not really a simple problem that I can give some one-line solution to (other than a simple ugly global that would probably break something else), but one of program structure. You don't magically get access to a variable that was created on another instance of another class. You have to either give the intelligence reference to MOS, or take it from BasicInfoPage, however that might happen. It seems to me that the classes are designed rather oddly-- an information page, for one thing, should not generate anything, and if it does, it should give it back to whatever needs to know-- some sort of central place, which should have been the one generating it in the first place. Ordinarily, you'd set the variables there, and get them from there. Or at least, I would.
If you want the basic answer of "how do I pass variables between different classes", then here you go, but I doubt it's exactly what you want, as you look to be using some sort of controlling framework:
class Foo(object):
def __init__(self, var):
self.var = var
class Bar(object):
def do_something(self, var):
print var*3
if __name__ == '__main__':
f = Foo(3)
b = Bar()
# look, I'm using the variable from one instance in another!
b.do_something(f.var)
If I understood you correctly, then the answer is: You can't.
intelligence should be an attribute of WizardPageSimple, if you'd want both classes to inherit it.
Depending on your situation, you might try to extract intelligence and related attributes into another baseclass. Then you could inherit from both:
class MOS(wiz.WizardPageSimple, wiz.IntelligenceAttributes): # Or something like that.
In that case you must use the co-operative super. In fact, you should be using it already. Instead of calling
wiz.WizardPageSimple.__init__(self, parent)
call
super(MOS, self).__init__(self, parent)