22. Glade och Gtk.Builder

Klassen Gtk.Builder ger dig möjligheten att designa användargränssnitt utan att skriva en enda rad kod. Detta är möjligt genom att beskriva gränssnittet i en XML-fil och sedan läsa in denna XML UI-definition under körtid med Builder-klassen vilken skapar objekten automatiskt. Använd programmet Glade för att inte behöva skriva denna XML manuellt, det låter dig skapa användargränssnittet på ett WYSIWYG-sätt (What You See Is What You Get, vad du ser är vad du får)

Denna metod har flera fördelar:

  • Mindre kod behöver skrivas.

  • Ändringar i användargränssnitten kan ses snabbare, så de kan förbättras.

  • Designers utan programmeringskunskap kan skapa och redigera användargränssnitt.

  • Beskrivningen av användargränssnittet är oberoende av programmeringsspråket som används.

Det finns fortfarande kod som krävs för att hantera gränssnittsändringar som utlöses av användaren, men Gtk.Builder låter dig fokusera på att implementera den funktionaliteten.

22.1. Skapa och läsa in .glade-filen

Först måste du hämta och installera Glade. Det finns flera handledningar om Glade, så det förklaras här inte i detalj. Låt oss starta med att skapa ett fönster med en knapp i, och spara till en fil med namnet example.glade. Den resulterande XML-filen bör se ut så här.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
      </object>
    </child>
  </object>
</interface>

För att läsa in denna fil i Python behöver vi ett Gtk.Builder-objekt.

builder = Gtk.Builder()
builder.add_from_file("example.glade")

Den andra raden läser in alla objekt som definieras i example.glade till Builder-objektet.

Det är också möjligt att bara läsa in några av objekten. Följande rad skulle bara lägga till de objekt (och deras barnobjekt) som anges i tupeln.

# we don't really have two buttons here, this is just an example
builder.add_objects_from_file("example.glade", ("button1", "button2"))

Dessa två metoder finns också för att läsa från en sträng snarare än en fil. Deras motsvarande namn är Gtk.Builder.add_from_string() och Gtk.Builder.add_objects_from_string() och de tar helt enkelt en XML-sträng istället för ett filnamn.

22.2. Komma åt komponenter

Nu när fönstret och knappen har lästs in vill vi också visa dem. Därför behöver metoden Gtk.Window.show_all() anropas på fönstret. Men hur kommer vi åt det associerade objektet?

window = builder.get_object("window1")
window.show_all()

Varje komponent kan erhållas från byggaren genom metoden Gtk.Builder.get_object() och komponentens id. Det är precis lätt.

Det är också möjligt att få en lista över alla objekt med

builder.get_objects()

22.3. Ansluta signaler

Glade möjliggör också att definiera signaler som du kan ansluta till hanterare i din kod utan att extrahera varje objekt från byggaren och ansluta till signalerna manuellt. Den första saken att göra är att deklarera signalnamnen i Glade. För detta exempel kommer vi agera när fönstret stängs och när knappen trycktes ned, så vi ger namnet ”onDestroy” till återanropet som hanterar fönstrets ”destroy”-signal och ”onButtonPressed” till återanropet som hanterar knappens ”pressed”-signal. Nu bör XML-filen se ut så här.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <object class="GtkWindow" id="window1">
    <property name="can-focus">False</property>
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </object>
</interface>

Nu behöver vi definiera hanterarfunktionerna i vår kod. onDestroy bör helt enkelt resultera i ett anrop till Gtk.main_quit(). När knappen trycks ned skulle vi vilja skriva ut strängen ”Hello World!”, så vi definierar hanteraren enligt följande

def hello(button):
    print("Hello World!")

Härnäst behöver vi ansluta signalerna och hanterarfunktionerna. Det lättaste sättet att göra det är att definiera en dict med en mappning från namnen till hanterarna och sedan skicka den till metoden Gtk.Builder.connect_signals().

handlers = {
    "onDestroy": Gtk.main_quit,
    "onButtonPressed": hello
}
builder.connect_signals(handlers)

Ett alternativt tillvägagångssätt är att skapa en klass som har metoder som anropas som signalerna. I vårt exempel skulle den sista kodsnutten kunna skrivas om som:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")
builder.connect_signals(Handler())

22.4. Exempel

Exemplets slutgiltiga kod

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class Handler:
    def onDestroy(self, *args):
        Gtk.main_quit()

    def onButtonPressed(self, button):
        print("Hello World!")


builder = Gtk.Builder()
builder.add_from_file("builder_example.glade")
builder.connect_signals(Handler())

window = builder.get_object("window1")
window.show_all()

Gtk.main()

22.5. Gtk.Template

Gtk.WidgetClass låter UI-definitionsfiler användas för att utöka en komponent, PyGObject tillhandahåller Gtk.Template som ett sätt att komma åt detta från Python.

UI-definitionsfilen som används i exemplet behöver en liten ändring för att inkludera ett <template>-element:

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires gtk+ 3.0 -->
  <template class="window1" parent="GtkWindow">
    <signal name="destroy" handler="onDestroy" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button</property>
        <property name="use-action-appearance">False</property>
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="receives-default">True</property>
        <property name="use-action-appearance">False</property>
        <signal name="pressed" handler="onButtonPressed" swapped="no"/>
      </object>
    </child>
  </template>
</interface>

Sedan kan den användas för att implementera exemplet med en Gtk.Window-underklass:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import gi

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


@Gtk.Template(filename="template_example.ui")
class Window1(Gtk.Window):
    __gtype_name__ = "window1"

    @Gtk.Template.Callback()
    def onDestroy(self, *args):
        Gtk.main_quit()

    @Gtk.Template.Callback()
    def onButtonPressed(self, button):
        print("Hello World!")


window = Window1()
window.show()

Gtk.main()

Mer information kan hittas på webbplatsen PyGObject.