Material Python. Benutzerdefinierte Karten mit OpenGL-Effekten


Grüße, liebe Liebhaber und Experten in Python!

In diesem Artikel werde ich Ihnen zeigen, wie Sie OpenGL-Effekte auf Ihre benutzerdefinierten Karten anwenden, wenn Sie plattformübergreifende Tools wie das Kivy- Framework und die Materialdesignbibliothek für dieses Framework, KivyMD , in Ihren Anwendungen verwenden . Lass uns gehen!

KivyMD verfügt über eine Standardkomponente MDCard - die Basisklasse zum Erstellen verschiedener benutzerdefinierter Karten ( Material Design Spec, Cards ). Wenn Sie nicht auf Details eingehen, befindet sich unter der Haube der MDCard das übliche BoxLayout - ein Container, mit dem Sie andere Widgets in vertikaler oder horizontaler Ausrichtung platzieren können. Das heißt, wenn Sie eine Art Karte erstellen müssen, z. B. Informationen über den Benutzer, tun Sie dies selbst. MDCard implementiert nur ripple_behavior , touch_behavior und wirft Schatten:

Ein Beispiel für ein Programm, das eine leere Karte anzeigt, lautet wie folgt:

from kivy.lang import Builder

from kivymd.app import MDApp

KV = '''
Screen:  #  

    MDCard:  # 
        #    
        size_hint: .6, .5
        pos_hint: {"center_x": .5, "center_y": .5}
'''


class TestCard(MDApp):
    def build(self):
        return Builder.load_string(KV)

TestCard().run()

Ergebnis:


Es sieht ziemlich einfach aus. Aber was ist, wenn wir eine schöne Karte mit einem Unschärfeeffekt wollen, wenn wir den Fokus erhalten? Wie zum Beispiel in der Anwendung Flutter UI Designs :


Muss es selbst machen! Darüber hinaus ist daran nichts Kompliziertes. Erstellen Sie zunächst die Basisklasse der zukünftigen Karte:

class RestaurantCard(MDCard):
    source = StringProperty()  #     
    shadow = StringProperty()  #    -
    text = StringProperty()  #  

Das Hauptbild der Karte:


Schattenbild:


Jetzt füllen wir die Karte mit Komponenten, deren Eigenschaften wir mithilfe der speziellen DSL-Sprache in Kv-Sprache ermittelt haben , die für die bequeme Gestaltung von Schnittstellenlayouts entwickelt wurde:

<RestaurantCard>
    elevation: 12

    RelativeLayout:

        # ,       .
        FitImage:  #   
            source: root.source

        FitImage:  # -
            source: root.shadow
            size_hint_y: None
            height: "120dp"

        MDLabel:  #  
            text: root.text
            markup: True
            size_hint_y: None
            height: self.texture_size[1]
            x: "10dp"
            y: "10dp"
            theme_text_color: "Custom"
            text_color: 1, 1, 1, 1

Wir haben das RelativeLayout- Widget in die Karte eingefügt , mit dem wir die Komponenten auf folgende Weise übereinander mischen können:


Zuerst haben wir das Hauptbild platziert, einen Schatten und Text darauf gelegt. Wenn wir nun unseren Code ausführen:

from kivy.lang import Builder
from kivy.properties import StringProperty

from kivymd.app import MDApp
from kivymd.uix.card import MDCard

KV = """
<RestaurantCard>
    elevation: 12

    RelativeLayout:

        FitImage:
            source: root.source

        FitImage:
            source: root.shadow
            size_hint_y: None
            height: "120dp"

        MDLabel:
            text: root.text
            markup: True
            size_hint_y: None
            height: self.texture_size[1]
            x: "10dp"
            y: "10dp"
            theme_text_color: "Custom"
            text_color: 1, 1, 1, 1


Screen:

    RestaurantCard:
        text: "[size=23][b]Restaurant[/b][/size]\\nTuborg Havnepark 15, Hellerup 2900 Denmark"
        shadow: "shadow-black.png"
        source: "restourant.jpg"
        pos_hint: {"center_x": .5, "center_y": .5}
        size_hint: .7, .5
"""


class RestaurantCard(MDCard):
    source = StringProperty()
    text = StringProperty()
    shadow = StringProperty()


class BlurCard(MDApp):
    def build(self):
        return Builder.load_string(KV)

BlurCard().run()

... wir bekommen das Ergebnis:


Und das Ergebnis ist natürlich weit vom erwarteten entfernt, da wir keinen Unschärfeeffekt oder abgerundete Kanten auf der Karte sehen. Beginnen wir mit dem Unschärfeeffekt. Kivy verfügt über ein Standard- EffectWidget- Widget , mit dem verschiedene Grafikeffekte auf seine untergeordneten Elemente angewendet werden können. Es funktioniert durch Rendern von Fbo-Instanzen mit benutzerdefinierten OpenGL-Shadern. Wir müssen den Unschärfeeffekt auf das Hauptbild und das Schattenbild auf der Karte anwenden. Daher müssen wir ihre Komponenten in das EffectWidget-Widget einfügen:

#:import effect kivy.uix.effectwidget.EffectWidget
#:import HorizontalBlurEffect kivy.uix.effectwidget.HorizontalBlurEffect

<RestaurantCard>

    ...

    RelativeLayout:
        
        #   ,      .
        EffectWidget:
            #  .
            effects: (HorizontalBlurEffect(size=root.blur),)

            FitImage:
                source: root.source

            FitImage:
                source: root.shadow
                size_hint_y: None
                height: "120dp"

    ...    

Fügen Sie ein Feld für den Wert des Unschärfeeffekts hinzu:

class RestaurantCard(MDCard):
    ...
    blur = NumericProperty(8)

Wir beginnen und sehen:


Wenn Sie mit der Maus über den Desktop fahren (wenn es sich um einen Desktop handelt) oder auf tippen (wenn er mobil ist), geschieht nichts. Damit die Karte auf das Ereignis on_focus reagiert, müssen wir das Lesen dieses Ereignisses in den Eigenschaften der RestaurantCard-Regel aktivieren und Methoden zuweisen, die ausgeführt werden, wenn dieses Ereignis registriert wird:

#:import Animation kivy.animation.Animation

<RestaurantCard>
    focus_behavior: True  #    on_focus
    # ,       .
    #   Animation,    .
    on_enter: Animation(blur=0, d=0.3).start(self)
    on_leave: Animation(blur=8, d=0.3).start(self)

Schon besser:


Um die Ecken der Karte zu kürzen, habe ich beschlossen, eine Schablone (Schablone) auf das EffectWidget-Widget anzuwenden:

#:import Stencil kivymd.uix.graphics.Stencil


#   ,   EffectWidget  Stencil.
<Effect@EffectWidget+Stencil>
    radius: [20,]


<RestaurantCard>
    ...

    RelativeLayout:

        Effect:
            ...

Und jetzt funktioniert alles wie geplant:


Vollständiger Beispielcode
from kivy.lang import Builder
from kivy.properties import StringProperty, NumericProperty

from kivymd.app import MDApp
from kivymd.uix.card import MDCard

KV = """
#:import Stencil kivymd.uix.graphics.Stencil
#:import Animation kivy.animation.Animation
#:import effect kivy.uix.effectwidget.EffectWidget
#:import HorizontalBlurEffect kivy.uix.effectwidget.HorizontalBlurEffect


<Effect@EffectWidget+Stencil>
    radius: [20,]


<RestaurantCard>
    md_bg_color: 0, 0, 0, 0
    elevation: 12
    focus_behavior: True
    on_enter: Animation(blur=0, d=0.3).start(self)
    on_leave: Animation(blur=8, d=0.3).start(self)
    radius: [20,]

    RelativeLayout:

        Effect:
            effects: (HorizontalBlurEffect(size=root.blur),)

            FitImage:
                source: root.source

            FitImage:
                source: root.shadow
                size_hint_y: None
                height: "120dp"

        MDLabel:
            text: root.text
            markup: True
            size_hint_y: None
            height: self.texture_size[1]
            x: "10dp"
            y: "10dp"
            theme_text_color: "Custom"
            text_color: 1, 1, 1, 1


FloatLayout:

    RestaurantCard:
        text: "[size=23][b]Restaurant[/b][/size]\\nTuborg Havnepark 15, Hellerup 2900 Denmark"
        shadow: "shadow-black.png"
        source: "restourant.jpg"
        pos_hint: {"center_x": .5, "center_y": .5}
        size_hint: .7, .5
"""


class RestaurantCard(MDCard):
    source = StringProperty()
    text = StringProperty()
    shadow = StringProperty()
    blur = NumericProperty(8)


class BlurCard(MDApp):
    def build(self):
        return Builder.load_string(KV)


BlurCard().run()


Nun, zum Schluss möchte ich ein Video zeigen, in dem zwei Programme funktionieren: eines, das mit dem Flutter-Framework geschrieben wurde, und das zweite - mit Kivy und KivyMD. Am Ende des Artikels hinterlasse ich eine Umfrage, in der Sie erraten müssen, welche Technologie wo verwendet wird.


All Articles