Kivy 语言可以访问继承的布局和小部件吗?
Posted
技术标签:
【中文标题】Kivy 语言可以访问继承的布局和小部件吗?【英文标题】:Can the Kivy language access inherited layouts and widgets? 【发布时间】:2016-12-03 15:08:14 【问题描述】:kivy 语言可以访问继承的布局和小部件吗?我想为我的小部件创建一个包含样式和标题标签的基本 BoxLayout。我希望能够从这个小部件继承并在不同位置添加其他小部件。
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<SimpleBar>:
canvas.before:
Color:
rgba: 0, 0.5, 0.5, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
id: my_layout
Label:
text: "hi"
<NewBar>:
Label:
text: "2"
''')
class SimpleBar(BoxLayout):
def log(self, value):
print(value)
class NewBar(SimpleBar):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
print(dir(self))
class GeneralApp(App):
def build(self):
return NewBar()
if __name__ == '__main__':
GeneralApp().run()
以上是我的基本运行小部件。
我希望 NewBar 的“2”标签位于 SimpleBar 的“hi”标签之前,如下所示。
<NewBar>:
BoxLayout:
id: my_layout
Label:
text: "2"
Label:
text: "hi"
我知道 - 可以否定项目。但是,<-NewBar>
删除了我的所有样式。
在 kivy 语言中有什么方法可以做到这一点吗?
【问题讨论】:
做了一点修改,现在它甚至支持索引了^^ 【参考方案1】:如果你想做一个复合小部件,接受新的孩子并将它们添加到一个特定的“容器”小部件中,你必须做一些 python。
基本上,这个想法是覆盖add_widget
,这样一旦有了小部件的基本结构,就可以使用新方法添加新的小部件。
假设你有这个NestingWidget
class NestingWidget(BoxLayout):
title = StringProperty()
def activate(self):
# do something
pass
使用这条规则
<NestingWidget>:
Label:
text: root.title
BoxLayout:
Button:
on_press: root.activate()
你想这样使用它:
FloatLayout:
NestingWidget:
title: 'first item'
Image:
source: '../examples/demo/pictures/images/Ill1.jpg'
这不会立即起作用,因为Image
将作为NestingWidget
的直接子级添加,所以它将位于Button
之下。
但是,您可能注意到 kivy 中的一些小部件可以接受新的嵌套小部件,而它们本身已经很复杂。
这样做的诀窍是——如前所述——覆盖 add_widget。
首先,让我们为容器添加一个 id。
<NestingWidget>:
Label:
text: root.title
BoxLayout:
id: container
Button:
on_press: root.activate()
那么,让我们在add_widget
中使用它。
class NestingWidget(BoxLayout):
…
def add_widget(self, *args, **kwargs):
if 'container' in self.ids:
return self.ids.container.add_widget(*args, **kwargs)
else:
return super(NestingWidget, self).add_widget(*args, **kwargs)
【讨论】:
【参考方案2】:这是一件有趣的事情:您不需要在 lang 本身中指定 kv lang 中使用的所有类 - 您也可以稍后在代码中使用 Factory.register
方法添加它们。这是一个例子:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.factory import Factory
from functools import partial
Builder.load_string('''
<MyWidget>:
Foo
Bar
''')
class MyWidget(BoxLayout):
pass
class MyApp(App):
def build(self):
Factory.register('Foo', cls=partial(Label, text='foo'))
Factory.register('Bar', cls=partial(Label, text='bar'))
return MyWidget()
if __name__ == '__main__':
MyApp().run()
让我们用它来创建一个模板基础小部件,我们稍后会填充各种内容。我们使用一个占位符,稍后用另一个小部件替换:
<BaseWidget>:
orientation: 'vertical'
Label:
size_hint: None, 0.1
text: 'title'
Placeholder
在 Python 代码中,我们在此基本模板类的 __init__
方法中注册了一个占位符类。
class BaseWidget(BoxLayout):
def __init__(self, **args):
# unregister if already registered...
Factory.unregister('Placeholder')
Factory.register('Placeholder', cls=self.placeholder)
super(BaseWidget, self).__init__(**args)
现在让我们定义一个内容类。
<TwoButtonWidget>:
Button:
text: 'button 1'
Button:
text: 'button 2'
最后创建一个自定义类,使用我们的基类作为模板,并将其占位符替换为内容类。这个类没有自己的 kivy 规则(这些规则被移到内容类中),所以当我们从基本模板继承时,不会插入额外的小部件。
# content class
class TwoButtonWidget(BoxLayout):
pass
# Base class subclass
class CustomizedWidget(BaseWidget):
placeholder = TwoButtonWidget # set contetnt class
一个完整的例子:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.factory import Factory
Builder.load_string('''
<BaseWidget>:
orientation: 'vertical'
widget_title: widget_title
placeholder: placeholder
Label:
size_hint: None, 0.1
id: widget_title
Placeholder
id: placeholder
<TwoButtonWidget>:
button1: button1
Button:
text: 'button 1'
id: button1
Button:
text: 'button 2'
<ThreeButtonWidget>:
orientation: 'vertical'
Button:
text: 'button a'
Button:
text: 'button b'
Button:
text: 'button c'
''')
class BaseWidget(BoxLayout):
def __init__(self, **args):
# unregister if already registered...
Factory.unregister('Placeholder')
Factory.register('Placeholder', cls=self.placeholder)
super(BaseWidget, self).__init__(**args)
class TwoButtonWidget(BoxLayout):
pass
class ThreeButtonWidget(BoxLayout):
pass
class CustomizedWidget1(BaseWidget):
placeholder = TwoButtonWidget
class CustomizedWidget2(BaseWidget):
placeholder = ThreeButtonWidget
class MyApp(App):
def build(self):
layout = BoxLayout()
c1 = CustomizedWidget1()
# we can access base widget...
c1.widget_title.text = 'First'
# we can access placeholder
c1.placeholder.button1.text = 'This was 1 before'
c2 = CustomizedWidget2()
c2.widget_title.text = 'Second'
layout.add_widget(c1)
layout.add_widget(c2)
return layout
if __name__ == '__main__':
MyApp().run()
您可以轻松扩展它,例如,拥有多个占位符。
将此应用于您的案例:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.factory import Factory
from functools import partial
Builder.load_string('''
<SimpleBar>:
canvas.before:
Color:
rgba: 0, 0.5, 0.5, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
Placeholder
Label:
text: "hi"
<NewBarContent>:
Label:
text: "2"
''')
class SimpleBar(BoxLayout):
def __init__(self, **args):
# unregister if already registered...
Factory.unregister('Placeholder')
Factory.register('Placeholder', cls=self.placeholder)
super(SimpleBar, self).__init__(**args)
class NewBarContent(BoxLayout):
pass
class NewBar(SimpleBar):
placeholder = NewBarContent
class MyApp(App):
def build(self):
return NewBar()
if __name__ == '__main__':
MyApp().run()
【讨论】:
【参考方案3】:使用简单的 kv 不,因为如果你在小部件中放置一些东西(例如 Label: ...
),它会调用 <widget>.add_widget()
方法,当这样的方法在没有 additional 参数的情况下调用时,它会默认放置小部件之后已经放置在它之前。因此,您可以搜索 kivy/lang/parser.py
文件并添加这样的功能(欢迎 PR),或者在 python 中执行它... __init__
或任何您想要添加小部件的地方(可能在某些事件之后) .
要在 python 中执行此操作,您可以根据文档调用 <widget (or self)>.add_widget(<child>, index=<where>)
。例如:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty
Builder.load_string('''
#:import Factory kivy.factory.Factory
<Ninja@Label>:
<SimpleBar>:
BoxLayout:
id: my_layout
Label:
text: "hi"
<ChildWithBenefits>:
placebefore:
[(Factory.Label(text='I am the first!'), 0),
(Factory.Ninja(text='No, I am!'), 2)]
''')
class SimpleBar(BoxLayout):
def log(self, value):
print(value)
class ChildWithBenefits(SimpleBar):
placebefore = ListProperty([])
def __init__(self, *args, **kwargs):
super(ChildWithBenefits, self).__init__(*args, **kwargs)
for child, index in self.placebefore:
print child.text
print type(child)
self.add_widget(child, index=index)
self.log('Hello!')
class GeneralApp(App):
def build(self):
return ChildWithBenefits()
if __name__ == '__main__':
GeneralApp().run()
【讨论】:
以上是关于Kivy 语言可以访问继承的布局和小部件吗?的主要内容,如果未能解决你的问题,请参考以下文章