TypeError:使用exec()时为super(type,obj)

时间:2019-05-03 00:19:11

标签: python kivy

下面是用Kivy编写的整个(测试)应用程序。 这类似于应用程序预览应用程序: 用户输入kv标记的文本(请参见变量self.kv)和类的文本(请参见变量self.text)。然后,他单击“预览”按钮,并在应用程序的右侧看到结果。 加载kv是使用kivy Builder.load_string()实现的。使用exec(,globals())实现类加载。

主要问题是,由于某些原因,当我第三次单击预览按钮时出现以下错误(前两次单击均无错误):

TypeError: super(type, obj): obj must be an instance or subtype of type

该错误可能是由于exec()引起的(没有exec,我不会收到此错误)。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button

KV = '''
BoxLayout:
    BoxLayout:
        orientation: 'vertical'

        CodeEd
            id: code_editor

        Button:
            text: 'Preview'
            on_release: app.preview()

    Preview:
        id: preview_area

<CodeEd@TextInput>
    text: app.text

<Preview@RelativeLayout>
'''

class MyApp(App):
    def build(self):
        self._kv_filename = 'KvEditor_internal.' + str(self.uid)

        self.text = '''
class MyButton(Button):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print (333)
        super(MyButton, self).on_touch_down(touch)


'''
        self.kv = 'MyButton'

        self.root = Builder.load_string(KV)


    def preview(self):
        preview_area = self.root.ids.preview_area

        #if 'MyButton' in globals():
        #    del globals()['MyButton']

        #print ('===================')
        #print ([i for i in dict(globals())])

        try:
            exec(self.text, globals())
        except:
            print ('some error when exec class ')

        Builder.unload_file(self._kv_filename)

        try:

            preview_area.add_widget(Builder.load_string(self.kv, filename=self._kv_filename))

        except Exception as e:
            print (e.message if getattr(e, r"message", None) else str(e))


MyApp().run()

如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

问题-预览区域是RelativeLayout

  

这似乎可行,但是您可以编辑或添加示例吗?我做   不需要增加预览区域中的按钮数量。我只是   每次按“预览”后都想在预览区域中   仅反映self.kv和self.text当前文本的内容。

示例

下面的示例应用了大多数增强功能,并且Preview区域是RelativeLayout

main.py

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.properties import NumericProperty, StringProperty
from kivy.factory import Factory

KV = '''
BoxLayout:
    BoxLayout:
        orientation: 'vertical'

        CodeEd
            id: code_editor

        Button:
            text: 'Preview'
            on_release: app.preview()

    Preview:
        id: preview_area

<CodeEd@TextInput>
    text: app.text

<Preview@RelativeLayout>
'''


class MyApp(App):
    text = StringProperty('')
    previous_text = StringProperty('')

    def build(self):
        self._kv_filename = 'KvEditor_internal.' + str(self.uid)

        self.text = '''
class MyButton(Button):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print (333)
        return super(Button, self).on_touch_down(touch)
'''

        self.previous_text = self.text
        self.kv = 'MyButton'

        self.root = Builder.load_string(KV)

    def preview(self):
        preview_area = self.root.ids.preview_area
        self.text = self.root.ids.code_editor.text

        try:
            # Class loading is implemented using exec(, globals())
            exec(self.text, globals())
        except:
            print('some error when exec class ')

        Builder.unload_file(self._kv_filename)

        try:
            # check for code changes
            if self.text != self.previous_text:
                Factory.unregister(self.kv)
                Factory.register(self.kv, cls=globals()[self.kv])
                total_children = len(preview_area.children)
                preview_area.clear_widgets()
                for child in range(total_children):
                    preview_area.add_widget(Builder.load_string(self.kv, filename=self._kv_filename))
                self.previous_text = self.text

            preview_area.add_widget(Builder.load_string(self.kv, filename=self._kv_filename))

        except Exception as e:
            print(e.message if getattr(e, r"message", None) else str(e))


MyApp().run()

输出

Result 1 - No changes to MyButton class Result 2 - Changes to MyButton class

问题1-TypeError

  

TypeError:super(type,obj):obj必须是的实例或子类型   输入

解决方案-TypeError

TypeError有两种解决方案。

方法1

super(MyButton, self).on_touch_down(touch)替换为return False

方法2

super(MyButton, self).on_touch_down(touch)替换为return super(Button, self).on_touch_down(touch)

问题2-支持代码更改为MyButton类

  

如果存在MyButton类,但我想对此进行更改,该怎么办   类,例如更改其某些方法等?

解决方案-支持代码更改为MyButton类

为了支持将代码更改为MyButton类,需要进行以下增强:

  • 添加Kivy Properties from kivy.properties import NumericProperty, StringProperty的导入语句
  • 为Kivy Factory对象from kivy.factory import Factory添加导入语句
  • 添加新的类属性previous_text,类型为StringProperty,以跟踪代码更改。
  • 初始化self.previous_text
  • 在调用方法self.text时更新preview()
  • 检查代码更改
  • 使用Kivy class MyButton注销先前的Factory
  • 使用Kivy class MyButton注册新的Factory
  • 保存添加的MyButton的总数
  • 删除以前使用MyButton添加的clear_widgets()。如果未删除先前添加的MyButton,则它将不具有新功能/代码更改的功能。
  • 使用for循环重新添加以前添加的MyButton,并添加新功能。
  • 分配新的代码更改,从self.textself.previous_text

摘要

from kivy.properties import NumericProperty, StringProperty
from kivy.factory import Factory
from kivy.logger import Logger
...
class MyApp(App):
    i = NumericProperty(0)
    text = StringProperty('')
    previous_text = StringProperty('')

    def build(self):
        ...
        self.text = '''
        ...
            return True    # consumed on_touch_down & don't propagate
        # return False
        return super(Button, self).on_touch_down(touch)
'''

        self.previous_text = self.text
        ...

    def preview(self):
        preview_area = self.root.ids.preview_area

        self.text = self.root.ids.code_editor.text
        ...
        try:
            # check for code changes
            if self.text != self.previous_text:
                Factory.unregister(self.kv)
                Factory.register(self.kv, cls=globals()[self.kv])
                total_children = len(preview_area.children)
                preview_area.clear_widgets()
                for child in range(total_children):
                    btn = Builder.load_string(self.kv, filename=self._kv_filename)
                    btn.text = str(child + 1)
                    preview_area.add_widget(btn)
                self.previous_text = self.text

示例

以下示例说明了一个代码编辑器,该代码编辑器支持将代码更改为MyButton类,并将MyButton小部件添加到GridLayout中。

main.py

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.button import Button
from kivy.logger import Logger
from kivy.factory import Factory

KV = '''
BoxLayout:
    BoxLayout:
        orientation: 'vertical'

        CodeEd
            id: code_editor

        Button:
            text: 'Preview'
            on_release: app.preview()

    Preview:
        id: preview_area

<CodeEd@TextInput>:
    text: app.text

<Preview@GridLayout>:
    cols: 3
'''


class MyApp(App):
    i = NumericProperty(0)
    text = StringProperty('')
    previous_text = StringProperty('')

    def build(self):
        self._kv_filename = 'KvEditor_internal.' + str(self.uid)

        self.text = '''
class MyButton(Button):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print(f"touch.pos={touch.pos}")
            print(f"Button.text={self.text}")
            return True    # consumed on_touch_down & don't propagate
        # return False
        return super(Button, self).on_touch_down(touch)
'''

        self.previous_text = self.text
        self.kv = 'MyButton'

        self.root = Builder.load_string(KV)

    def preview(self):
        preview_area = self.root.ids.preview_area

        self.text = self.root.ids.code_editor.text

        try:
            # Class loading is implemented using exec(, globals())
            exec(self.text, globals())
        except Exception as msg:
            print('\nException: some error when exec class ')
            Logger.error("KivyApp: Exception: some error when exec class")
            print(msg)
            quit()

        Builder.unload_file(self._kv_filename)

        try:
            # check for code changes
            if self.text != self.previous_text:
                Factory.unregister(self.kv)
                Factory.register(self.kv, cls=globals()['MyButton'])
                total_children = len(preview_area.children)
                preview_area.clear_widgets()
                for child in range(total_children):
                    btn = Builder.load_string(self.kv, filename=self._kv_filename)
                    btn.text = str(child + 1)
                    preview_area.add_widget(btn)
                self.previous_text = self.text

            self.i += 1
            btn = Builder.load_string(self.kv, filename=self._kv_filename)
            btn.text = str(self.i)
            preview_area.add_widget(btn)

        except Exception as e:
            print(e.message if getattr(e, r"message", None) else str(e))


MyApp().run()

输出

Result1 - No code changed Result2 - Code changed