2016-07-29 34 views
9

Czy język kivy może uzyskać dostęp do dziedziczonych układów i widżetów? Chcę utworzyć jeden podstawowy BoxLayout zawierający etykietę tytułową i tytułową mojego widgetu. Chcę móc dziedziczyć z tego widgetu i dodawać dodatkowe widżety w różnych pozycjach.Czy język Kivy może uzyskać dostęp do dziedziczonych układów i widżetów?

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

Powyżej znajduje się mój podstawowy widget do biegania.

Chcę, aby etykieta "2" NewBar znajdowała się przed etykietą "cześć" SimpleBar, jak poniżej.

Wiem, że - może negować przedmioty. Jednak <-NewBar> usuwa całą moją stylizację.

Czy można to zrobić w języku kivy?

+0

dokonał niewielkiej edycji, obsługuje nawet teraz wskaźniki^^ – KeyWeeUsr

Odpowiedz

2

Z prostego kV nie, bo jeśli umieścisz coś w widgecie (np Label: ...), będzie to nazwać <widget>.add_widget() sposób i kiedy taka metoda jest wywoływana bez additional parametrów, to będzie domyślnie miejscu już widżet po co było umieszczone przed nim. Dlatego możesz albo przeszukać plik kivy/lang/parser.py i dodać taką funkcjonalność (PR welcome), albo zrobić to w pythonie w hm ... __init__ lub gdziekolwiek chcesz dodać widżet (może po jakimś wydarzeniu).

Aby to zrobić w Pythonie, można zadzwonić pod numer <widget (or self)>.add_widget(<child>, index=<where>) zgodnie z dokumentami. Na przykład:

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 
<[email protected]>: 

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

Oto zabawna rzecz: nie musisz określić wszystkie klasy używane w kv lang w samej lang - można również dodać je za pomocą metody Factory.register później w kodzie. Oto przykład:

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

Wykorzystajmy go do stworzenia podstawowego widgetu szablonu, który później wypełniamy różnymi treściami. Używamy zastępczy, który później możemy wymienić z innym widget:

<BaseWidget>: 
    orientation: 'vertical' 
    Label: 
     size_hint: None, 0.1 
     text: 'title' 
    Placeholder 

W Pythonie możemy zarejestrować klasę zastępczy w metodzie tej klasy szablonu baza __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) 

Zdefiniujmy teraz klasę treści.

<TwoButtonWidget>: 
    Button: 
     text: 'button 1' 
    Button: 
     text: 'button 2' 

Na koniec utwórz niestandardową klasę, która wykorzystuje naszą klasę podstawową jako szablon i zastępuje obiekt zastępczy klasą treści. Ta klasa nie ma własnych reguł kivy (są one przenoszone do klasy treści), więc gdy dziedziczymy z naszego szablonu podstawowego, nie dodaje się żadnych dodatkowych widgetów.

# content class 
class TwoButtonWidget(BoxLayout): 
    pass 

# Base class subclass 
class CustomizedWidget(BaseWidget): 
    placeholder = TwoButtonWidget # set contetnt class 

pełny przykład:

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

można łatwo rozszerzyć go i na przykład, mieć wiele zastępczych.

Stosując to do sprawy:

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

Jeśli chcesz zrobić kompozytowego widżet, że przyjęcie nowych dzieci i dodać je do jednego konkretnego „pojemnik” widget, trzeba zrobić kilka pytona.

Zasadniczo chodzi o zastąpienie add_widget, więc gdy tylko pojawi się podstawowa struktura widgetu, nowe widżety zostaną dodane przy użyciu nowej metody.

Powiedzmy masz ten NestingWidget

class NestingWidget(BoxLayout): 
    title = StringProperty() 

    def activate(self): 
     # do something 
     pass 

tej zasady

<NestingWidget>: 
    Label: 
     text: root.title 

    BoxLayout: 

    Button: 
     on_press: root.activate() 

i chcesz go używać tak:

FloatLayout: 
    NestingWidget: 
     title: 'first item' 
     Image: 
      source: '../examples/demo/pictures/images/Ill1.jpg' 

to nie będzie działać natychmiast, ponieważ Image zostanie dodane jako bezpośrednie dziecko z NestingWidget, więc będzie pod Button.

Howether, pewnie zauważyłeś, że niektóre widżety w kivy mogą akceptować nowe zagnieżdżone widżety, a jednocześnie są już złożone.

Aby to zrobić, należy - jak wspomniano wcześniej - zastąpić add_widget.

Najpierw dodajmy identyfikator do naszego kontenera.

<NestingWidget>: 
    Label: 
     text: root.title 

    BoxLayout: 
     id: container 

    Button: 
     on_press: root.activate() 

następnie użyjmy go w 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)