-1

I need to change bases of some of my widget class, depending on passed arguments.

The simplest example is:

class A:
    def a(self):
        print (1)

class B:
    def __init__(self):
        self.extend_instance(A)
        self.a()

    def extend_instance(obj, cls):
        base_cls = obj.__class__
        base_cls_name = obj.__class__.__name__
        obj.__class__ = type(base_cls_name, (base_cls, cls),{})

B()

Maybe not an optimal way, because it changes bases every time when a new B instance created (if I undestand it right). But it seems to work.

This is a very simplified example (without arguments, depending on which class B instances with different bases should be created.)

Now I'm trying to implement this with kivy widgets

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label


class CoolLabel(Label):
    def a(self):
        print (1)


class MyLabel:
    def __init__(self, **kwargs):

        #self.__basses__ = CoolLabel

        self.extend_instance(CoolLabel)

        self.a()


    def extend_instance(obj, cls):
        base_cls = obj.__class__
        base_cls_name = obj.__class__.__name__
        obj.__class__ = type(base_cls_name, (base_cls, cls),{})


class MyApp(App):
    def build(self):
        self.root = MyLabel(text= '123456')


MyApp().run()

And I get this error:

   File "main.py", line 24, in extend_instance
     obj.__class__ = type(base_cls_name, (base_cls, cls),{})
 TypeError: __class__ assignment: 'MyLabel' object layout differs from 'MyLabel'

what is the reason? can this be related to kivy Widget Metaclass? and are there any ways to workaround this?

Update:

Let me simplify my problem. The main question is - why this doesn't work?

from kivy.uix.widget import Widget

class MyWidget:
    def __init__(self, **kwargs):
        self.__class__ = type('MyWidget', (Widget,),{})

MyWidget()

Here's the error I have:

   File "main.py", line 7, in <module>
     MyWidget()
   File "main.py", line 5, in __init__
     self.__class__ = type('MyWidget', (Widget,),{})
 TypeError: __class__ assignment: 'MyWidget' object layout differs from 'MyWidget'
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
me2 beats
  • 774
  • 1
  • 8
  • 24
  • Is there a reason why you are using that extend_instance function instead of just defining B to inherit from A? For example, `class B(A):` automatically introduces everything from B into A – Erik Oct 23 '19 at 03:56
  • yes let me explain. Suppose, for example, I want to have a class MyLabel, then kivy, depending on the arguments I passed, imports and uses a suitable widget. it can be a widget that is quite different in structure from a regular label, but they all have common attributes inherent in the label, such as text, text color, etc. I pre-create these widgets, but I want the kivy to choose which is better to use (I also prescribe these rules). – me2 beats Oct 23 '19 at 04:20
  • @me2beats Read https://stackoverflow.com/questions/3308792/python-object-layout and https://stackoverflow.com/questions/13280680/how-dangerous-is-setting-self-class-to-something-else, it seems to me that something simpler is to implement the factory pattern as one of the answers to the last question shows https://stackoverflow.com/questions/13280680/how-dangerous-is-setting-self-class-to-something-else – eyllanesc Oct 24 '19 at 03:44
  • oh, so this can be because some Widget bases (for example EventDispatcher) written in cython, right? – me2 beats Oct 24 '19 at 03:53

1 Answers1

0

looks like this works:

from kivy.event import EventDispatcher

class SomeWidget(EventDispatcher):
    def a(self):
        print ('hi')

class MyWidget(EventDispatcher):
    def __init__(self, **kwargs):
        self.__class__ = type('MyWidget', (SomeWidget,),{})
        self.a()

MyWidget()

although I still do not understand what this connected with. I have just assumed (thanks to @eyllanesc comment) that the error was due to the incompatibility of bases, somehow related to that kivy EventDispatcher is written in cython

me2 beats
  • 774
  • 1
  • 8
  • 24