下面是用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()
如何解决这个问题?
答案 0 :(得分:1)
这似乎可行,但是您可以编辑或添加示例吗?我做 不需要增加预览区域中的按钮数量。我只是 每次按“预览”后都想在预览区域中 仅反映self.kv和self.text当前文本的内容。
下面的示例应用了大多数增强功能,并且Preview
区域是RelativeLayout
。
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()
TypeError:super(type,obj):obj必须是的实例或子类型 输入
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)
如果存在MyButton类,但我想对此进行更改,该怎么办 类,例如更改其某些方法等?
为了支持将代码更改为MyButton
类,需要进行以下增强:
from kivy.properties import NumericProperty, StringProperty
的导入语句from kivy.factory import Factory
添加导入语句previous_text
,类型为StringProperty
,以跟踪代码更改。self.previous_text
self.text
时更新preview()
。class MyButton
注销先前的Factory
class MyButton
注册新的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
中。
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()