Kevin
Kevin

Reputation: 716

Super() python not working for subclass tkinter

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

Upvotes: 0

Views: 795

Answers (4)

teletypist
teletypist

Reputation: 321

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.

Upvotes: 0

Nir Alfasi
Nir Alfasi

Reputation: 53525

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

Upvotes: 0

Marcin
Marcin

Reputation: 238131

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

Upvotes: 2

Martin Konecny
Martin Konecny

Reputation: 59601

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):
            ...

Upvotes: 0

Related Questions