【问题标题】:Can the Kivy language access inherited layouts and widgets?Kivy 语言可以访问继承的布局和小部件吗?
【发布时间】: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"

我知道 - 可以否定项目。但是,&lt;-NewBar&gt; 删除了我的所有样式。

在 kivy 语言中有什么方法可以做到这一点吗?

【问题讨论】:

  • 做了一点修改,现在它甚至支持索引了^^

标签: python kivy kivy-language


【解决方案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: ...),它会调用 &lt;widget&gt;.add_widget() 方法,当这样的方法在没有 additional 参数的情况下调用时,它会默认放置小部件之后已经放置在它之前。因此,您可以搜索 kivy/lang/parser.py 文件并添加这样的功能(欢迎 PR),或者在 python 中执行它... __init__ 或任何您想要添加小部件的地方(可能在某些事件之后) .

      要在 python 中执行此操作,您可以根据文档调用 &lt;widget (or self)&gt;.add_widget(&lt;child&gt;, index=&lt;where&gt;)。例如:

      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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        • 1970-01-01
        • 2013-02-20
        • 2014-04-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多