TypeError: super (type, obj) при использовании exec () - PullRequest
0 голосов
/ 03 мая 2019

Ниже приведено целое (тестовое) приложение, написанное с помощью Kivy.Это что-то вроде приложения предварительного просмотра: пользователь вводит текст разметки kv (см. Переменную self.kv) и текст классов (см. Переменную self.text).Затем он нажимает кнопку «Предварительный просмотр» и видит результат в правой части приложения.Загрузка kv осуществляется с помощью kivy Builder.load_string ().Загрузка классов осуществляется с помощью exec (, globals ()).

Основная проблема заключается в том, что по какой-то причине я получаю следующую ошибку, когда нажимаю на кнопку предварительного просмотра в третий раз (первые 2 щелчка работают безошибки):

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 Ответ

1 голос
/ 03 мая 2019

Вопрос - область предварительного просмотра - 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: 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().
  • Проверка изменений кода
  • Отменить регистрацию предыдущего class MyButton с использованием Kivy Factory
  • Зарегистрировать новый class MyButton используя Kivy Factory
  • Сохранить общее количество MyButton добавлено
  • Удалить ранее добавленные MyButton, используя clear_widgets(). Если ранее добавленные MyButton не удаляются, функциональность новых функций / изменений кода не будет.
  • Используйте цикл for для повторного добавления ранее добавленного MyButton с новыми функциями / возможностями.
  • Назначить новые изменения кода, self.text на self.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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...