So I have a class, Application, with 2 subclasses MyButton and MyLabel. Application also has
self.backgroundcolor = 'orange'
self.textcolor = 'black'
I want to use these two variables in my subclasses MyButton and MyLabel. So, I tried
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
but it doesn't work, saying that
TypeError: super(type, obj): obj must be an instance or subtype of type
But my Application class looks like
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
MORE STUFF HERE...
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
Nested class cant access directly attributes of outer class. You need to do it indirectly, for example:
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
# create button and label and pass the application instance
# so that they can reference its attributes and methods
self.my_button = MyButton(self)
self.my_label = MyLabel(self)
class MyButton(Button):
def __init__(self, app_instance, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = app_instance.backgroundcolor
self['fg'] = app_instance.textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, app_instance, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = app_instance.textcolor
So I have a class, Application, with 2 subclasses MyButton and MyLabel.
MyButton and MyLabel do not appear to be subclasses of Application. You can only call super() to access Application from subclasses.
For example, to make MyLabel a subclass of Application
class MyLabel(Application):
def __init__(self, *args, **kwargs):
...
In the call to super() you have to pass your type and "yourself" (self):
so instead of doing:
super(Application, self)
you should do:
super(MyButton, self)
Hence the error, obj must be an instance or subtype of type:
self is not an instance nor subtype of Application
There is a confusion in this example between a nested class, which is a class defined within another class, and a subclass, which is a class defined by extending an existing class.
In Python, class inheritance (upon which the super method is based) is accomplished by defining a class with the super class provided as a parameter:
class Application(Frame):
... #Some class definition
class MyButton(Application):
... #MyButton is now a subclass of Application.
But this is not what you want, since you want to inherit the behaviour of the Tkinter Button and Label classes for your MyButton and MyLabel classes respectively (rather than inheriting the behaviour of the Application class.
You original attempt using nested classes doesn't appear as necessarily a bad idea, since it would neatly package all of the behaviour of your class into one place, but it has serious drawbacks which probably aren't what you want.
For a start, you cannot reference an instance of the Application class from the nested classes without injecting it in somehow, such as during initialisation. You can, however, access the properties if they are class properties, defined in the Application class namespace, just like you nested classes. This is probably confusing so here is an example:
class Application(object):
classvar = "Foo" #This is a class level variable
class NestedClass(object):
#This is now a nested class, accessed using Application.NestedClass
def __init__(self, app):
#This will fail, classvar doesn't exist in this scope
self.var = classvar
#This will work if app is an instance of Application
#Or if app is the class itself
self.var = app.classvar
#This will always work, since it references the Application
#class directly but it won't capture changes introduced
#by an instance of Application, which is what you'll probably do
self.var = Application.classvar
Class level behaviour becomes very confusing due to scoping, and nested classes are even more confusing for nothing that can't be gained from implementing every class at the module level.
The best way to inject this kind of requirement is to do it the very way Tkinter does it itself. Hand off the Application instance as the master of the widget instance it creates. This is shown in Marcin's answer.
Related
class main():
def __init__(self):
self.root = tk.Tk()
# Icono
self.notebook=ttk.Notebook(self.root)
self.notebook.pack(fill='both',expand='yes')
# Crear Frames blancos
tab_FBR1=tk.Frame(self.notebook,bg='white') #
tab_FBR2=tk.Frame(self.notebook,bg='white') #
tab_FBR3=tk.Frame(self.notebook,bg='white') #
#ASIGNACIÓN PESTAÑAS FBR1,FBR2 Y FBR3
self.notebook.add(tab_FBR1,text='FBR1') #
self.notebook.add(tab_FBR2,text='FBR2') #
self.notebook.add(tab_FBR3,text='FBR3') #
# Configurations FBR1, FBR2 y FBR3
self.window_FBR(tab_FBR1)
self.window_FBR(tab_FBR2)
self.window_FBR(tab_FBR3)
I want to create 3 windows calling a method called def window_FBR, to create 3 windows with their own variables.
def window_FBR(self,tab):
self.rcolor=tk.IntVar(value=4)
tk.Radiobutton(tab, text="Red", variable=self.rcolor, value=1, command=self.color_rojo(tab),font=("arial",10),bg=('white')).place(x=10,y=70)
However is not working, have you guys have some ideas about how to manage the variables, in the method to create different variables each time I am calling the method?
many thanks
I want to create a GUI in Tkinter with 3 windows.
I have a problem because when the variables are created, they did not start with the default value for the self.rcolor=tk.IntVar(value=4)
My solution for multiple windows that have their own variables is to create an independent class that takes its own parameters, and has its own subclasses if necessary, you can pass one or several different parameters in each case, hope that helps.
from tkinter import *
class SubWin(Tk):
def __init__(self, string, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('300x300')
label = Label(master=self, text=string)
label.pack()
class App(Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry('300x300')
sub1 = SubWin(string='Helooo1')
sub2 = SubWin(string='Helooo2')
if __name__ == '__main__':
app = App()
app.mainloop()
I have a Tkinter App and currently I am trying to split the code from each tab to be on each tab. I can pass the string as a parameter into the Tab but is it possible to access the parent variables from the child ttk.Frame ? The actual code has more things.
class Tab(ttk.Frame):
def __init__(self, master):
super().__init__(master)
def PrintThings(self):
Print(self._A) ##How can I access here self._A to print Hello World
class App():
def __init__(self):
self._A = "Hello World"
self._root = Tk()
self._root.geometry("800x1200")
self._TabContainer = ttk.Notebook(self._root)
self._Tab = Tab(self._TabContainer)
def main(self):
self._Tab.pack()
App.main()
The easiest solution is to pass the instance of App to each tab. With that, the tab has access to anything defined in App.
It looks something like this:
class App():
...
self._Tab = Tab(self._TabContainer, app=self)
...
...
class Tab(ttk.Frame):
def __init__(self, master, app):
self.app = app
...
def PrintThings(self):
print(self.app._A)
from tkinter import *
from tkinter.ttk import *
class window(Tk):
def __init__(self, **kwargs):
Tk.__init__(self, **kwargs)
box=Frame(self).pack(fill=BOTH,expand=1)
login=Frame(box,self).pack(fill=BOTH,expand=1)
register=Frame(box,self).pack(fill=BOTH,expand=1)
self.fup(login)
def fup(self,f):
f.tkraise()
class login(Frame):
def __init__(self, parent, controller,**kwargs):
Frame.__init__(self, parent)
super().__init__(**kwargs)
lab=Label(self,text='Login').grid(row=0,column=3,pady=3)
but=Button(self,text='Signup',command=lambda : controller.fup(register)).grid(row=4,column=0)
class register(Frame):
def __init__(self, parent, controller,**kwargs):
Frame.__init__(self, parent)
super().__init__(**kwargs)
lab=Label(self,text='Register').grid(row=0,column=3,pady=3)
but=Button(self,text='<<',command=lambda : controller.fup(login)).grid(row=0,column=0)
win=window()
win.mainloop()
You have a number of problems here. First, you're confused about how classes and class objects work. When you said
login=Frame(box,self).pack(fill=BOTH,expand=1)
That does not create an object of class login. It creates a simple Frame. In this case, it doesn't store an object at all, because the pack method does not return anything. In your login class, you refer to register, but that isn't an object, it's a class.
This is closer to what you want, and brings up a window, but since I can't tell what you really wanted, you'll have to take it from here.
from tkinter import *
from tkinter.ttk import *
class login(Frame):
def __init__(self, parent, controller,**kwargs):
super().__init__(parent, **kwargs)
lab=Label(self,text='Login').grid(row=0,column=3,pady=3)
but=Button(self,text='Signup',command=lambda : controller.fup(controller.register)).grid(row=4,column=0)
class register(Frame):
def __init__(self, parent, controller,**kwargs):
super().__init__(parent, **kwargs)
lab=Label(self,text='Register').grid(row=0,column=3,pady=3)
but=Button(self,text='<<',command=lambda : controller.fup(controller.login)).grid(row=0,column=0)
class window(Tk):
def __init__(self, **kwargs):
Tk.__init__(self, **kwargs)
box=Frame(self)
box.pack(fill=BOTH,expand=1)
self.login=login(box,self)
self.login.pack(fill=BOTH,expand=1)
self.register=register(box,self)
self.register.pack(fill=BOTH,expand=1)
self.fup(self.login)
def fup(self,f):
f.tkraise()
win=window()
win.mainloop()
I'm trying to use a variable in class A in class B by using __init__() method. But somehow the variable in class A and B couldn't show up (no change in my cvs write file). Here is my code:
class Welcome(object):
csv_name_sub = None
csv_name_ses = None
def __init__(self, master, csv_name_sub = None, csv_name_ses = None):
self.entrySub=Entry(self.master,bg="grey")
self.entrySes=Entry(self.master,bg="grey")
self.csv_name_sub = str(self.entrySub.get())
self.csv_name_ses = str(self.entrySes.get())
def writeToFile(self):
with open("/Users/Desktop/" + self.csv_name_sub+ '_' + self.csv_name_ses + '_subjectNo.csv',
'w') as resultFile:
resultFileWrite=....
class App(Welcome):
def __init__(self, master):
Welcome.__init__(self, master) #I'm using python 3.5
Welcome.csv_name_sub = str(self.entrySub.get())
Welcome.csv_name_ses = str(self.entrySes.get())
print('session is', Welcome.csv_name_ses)
print("subject is", Welcome.csv_name_sub)
self.resultFile = open("/Users/Desktop/" + Welcome.csv_name_sub + '_' + Welcome.csv_name_ses + '_results.csv', 'w')
The program can be run without an error, but csv_name_sub and csv_name_ses is empty when I print them in these two classes.
The updated attributes are bound to the instance not the superclass. You should reference the attributes via the instance using self:
class App(Welcome):
def __init__(self, master):
Welcome.__init__(self, master) # Updated attributes are bound to self
self.csv_name_sub = str(self.entrySub.get())
self.csv_name_ses = str(self.entrySes.get())
...
Use the attributes of the instance, i.e. from self:
class App(Welcome):
def __init__(self, master):
super().__init__(master) #I'm using python 3.
print('session is', self.csv_name_ses)
print("subject is", self.csv_name_sub)
Working example:
class Entry(object):
def __init__(self, master, bg):
pass
def get(self):
return 42
class Welcome(object):
def __init__(self, master):
self.master = master
self.entrySub = Entry(self.master, bg="grey")
self.entrySes = Entry(self.master, bg="grey")
self.csv_name_sub = str(self.entrySub.get())
self.csv_name_ses = str(self.entrySes.get())
class App(Welcome):
def __init__(self, master):
super().__init__(master)
print('session is', self.csv_name_ses)
print("subject is", self.csv_name_sub)
app = App('x')
Output:
session is 42
subject is 42
test.kv
<RootWidget>:
test: test
Label:
id: test
text: "some"
test.py
class RootWidget(Widget):
test = ObjectProperty(None)
def __init__(self, **kwargs):
# 1:
print(self.test) # None
# 2:
def after_tick(*args):
print(self.test) # Label object
Clock.schedule_once(after_tick, 0)
super().__init__()
If I'll try to bind something to self.test directly inside __init__ (1), I would get AttributeError since kv rules aren't applied yet and self.test is None. Possible workaround would be to bind to attribute after first event loop iteration (2).
This solution seems to be duct tape. Is there any better way?
I know 3 other ways.
First, make the after_tick a method and execute it in init:
class RootWidget(Widget):
test = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.delayed_init()
#mainthread
def delayed_init(self):
print(self.text)
Second, use a built-in method that is always executed after the first loop frame:
class RootWidget(Widget):
test = ObjectProperty(None)
def add_widget(self, widget, index=0):
super().add_widget(widget, index)
if widget.__class__.__name__ == 'Label':
widget.text = self.test.text
This is useful if you have a lot of same type widgets to bind.
Third, use a property's on_x callback:
class RootWidget(Widget):
test = ObjectProperty(None)
def on_test(self, *args):
print(self.test)