6. Layoutbehållare¶
Medan många verktygslådor för grafiska användargränssnitt tvingar dig att precist placera komponenter i ett fönster med absolut positionering, så använder GTK+ ett annat tillvägagångssätt. Snarare än att ange positionen och storleken för varje komponent i fönstret så kan du arrangera dina komponenter i rader, kolumner och/eller tabeller. Storleken på ditt fönster kan avgöras automatiskt, baserat på storleken för de komponenter som det innehåller. Storlekarna för komponenterna avgörs i sin tur av mängden text som de innehåller, eller minimi- och maximistorlekarna som du anger, och/eller hur du har begärt att det tillgängliga utrymmet ska delas mellan uppsättningar av komponenter. Du kan göra din layout bättre genom att ange utfyllnadsavstånd och centreringsvärden för var och en av dina komponenter. GTK+ använder sedan all denna information för att ändra storlek och position för allt på ett förnuftigt och smidigt sätt när användaren manipulerar fönstret.
GTK+ arrangerar komponenter hierarkiskt, med behållare. De är osynliga för slutanvändaren och infogas i ett fönster, eller placeras i varandra för att skapa en layout för komponenter. Det finns två sorters behållare: behållare med ett barn, vilka alla är ättlingar till Gtk.Bin
, och behållare med flera barn, vilka är ättlingar till Gtk.Container
. De mest använda är vertikala eller horisontella rutor (Gtk.Box
) och rutnät (Gtk.Grid
).
6.1. Rutor¶
Rutor är osynliga behållare som vi kan packa våra komponenter i. Då komponenter packas i en horisontell ruta infogas objekten horisontellt från vänster till höger eller höger till vänster beroende på huruvida Gtk.Box.pack_start()
eller Gtk.Box.pack_end()
används. I en vertikal ruta packas komponenter uppifrån och ner eller omvänt. Du kan använda alla tänkbara kombinationer av rutor i eller bredvid andra rutor för att skapa önskad effekt.
6.1.1. Exempel¶
Låt oss ta en titt på en något ändrad version av det utökade exemplet med två knappar.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Hello World")
self.box = Gtk.Box(spacing=6)
self.add(self.box)
self.button1 = Gtk.Button(label="Hello")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.button1, True, True, 0)
self.button2 = Gtk.Button(label="Goodbye")
self.button2.connect("clicked", self.on_button2_clicked)
self.box.pack_start(self.button2, True, True, 0)
def on_button1_clicked(self, widget):
print("Hello")
def on_button2_clicked(self, widget):
print("Goodbye")
win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
Vi skapar först en horisontellt orienterad rutbehållare där 6 bildpunkter placeras mellan barn. Denna ruta blir barnet till toppnivåfönstret.
self.box = Gtk.Box(spacing=6)
self.add(self.box)
Därefter lägger vi till två olika knappar till rutbehållaren.
self.button1 = Gtk.Button(label="Hello")
self.button1.connect("clicked", self.on_button1_clicked)
self.box.pack_start(self.button1, True, True, 0)
self.button2 = Gtk.Button(label="Goodbye")
self.button2.connect("clicked", self.on_button2_clicked)
self.box.pack_start(self.button2, True, True, 0)
Medan komponenter positioneras från vänster till höger med Gtk.Box.pack_start()
, så positioneras de från höger till vänster med Gtk.Box.pack_end()
.
6.2. Grid¶
Gtk.Grid
är en behållare som arrangerar sina barnkomponenter i rader och kolumner, men du behöver inte ange dimensionerna i konstruktorn. Barn läggs till med Gtk.Grid.attach()
. De kan spänna över flera rader eller kolumner. Metoden Gtk.Grid.attach()
tar fem parametrar:
Parametern
child
är denGtk.Widget
som ska läggas till.left
är kolumnnumret att fästa vänster sida avchild
till.top
indikerar radnumret att fästa översidan avchild
till.width
ochheight
indikerar antalet kolumner respektive rader somchild
kommer spänna över.
Det är också möjligt att lägga till ett barn intill ett befintligt barn, med Gtk.Grid.attach_next_to()
, vilket också tar fem parametrar:
child
är denGtk.Widget
som ska läggas till, som ovan.sibling
är en befintlig barnkomponent tillself
(enGtk.Grid
-instans) ellerNone
.child
-komponenten kommer placeras intillsibling
, eller omsibling
ärNone
, i början eller slutet av rutnätet.side
är enGtk.PositionType
som indikerar vilken sida avsibling
somchild
positioneras intill.width
ochheight
indikerar antalet kolumner respektive rader som komponentenchild
kommer spänna över.
Slutligen kan Gtk.Grid
användas som en Gtk.Box
genom att helt enkelt använda Gtk.Grid.add()
, vilken kommer placera barn intill varandra i den riktning som avgörs av egenskapen ”orientation” (har standardvärdet Gtk.Orientation.HORIZONTAL
).
6.2.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class GridWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Grid Example")
button1 = Gtk.Button(label="Button 1")
button2 = Gtk.Button(label="Button 2")
button3 = Gtk.Button(label="Button 3")
button4 = Gtk.Button(label="Button 4")
button5 = Gtk.Button(label="Button 5")
button6 = Gtk.Button(label="Button 6")
grid = Gtk.Grid()
grid.add(button1)
grid.attach(button2, 1, 0, 2, 1)
grid.attach_next_to(button3, button1, Gtk.PositionType.BOTTOM, 1, 2)
grid.attach_next_to(button4, button3, Gtk.PositionType.RIGHT, 2, 1)
grid.attach(button5, 1, 2, 1, 1)
grid.attach_next_to(button6, button5, Gtk.PositionType.RIGHT, 1, 1)
self.add(grid)
win = GridWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
6.3. ListBox¶
En Gtk.ListBox
är en vertikal behållare som innehåller Gtk.ListBoxRow
-barn. Dessa rader kan sorteras och filtreras dynamiskt, och rubriker kan läggas till dynamiskt beroende på radinnehållet. Den tillåter också tangentbords- och musnavigering samt markering som en typisk lista.
Att använda Gtk.ListBox
är ofta ett alternativ till Gtk.TreeView
, särskilt när listinnehållet har en mer komplicerad layout än vad som tillåts av en Gtk.CellRenderer
, eller när innehållet är interaktivt (t.ex. har en knapp i sig).
Även om en Gtk.ListBox
endast får ha barn som är Gtk.ListBoxRow
, så kan du lägga till valfri komponent till den med Gtk.Container.add()
, så kommer automatiskt en Gtk.ListBoxRow
-komponent infogas mellan listan och komponenten.
6.3.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class ListBoxRowWithData(Gtk.ListBoxRow):
def __init__(self, data):
super().__init__()
self.data = data
self.add(Gtk.Label(label=data))
class ListBoxWindow(Gtk.Window):
def __init__(self):
super().__init__(title="ListBox Demo")
self.set_border_width(10)
box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(box_outer)
listbox = Gtk.ListBox()
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
box_outer.pack_start(listbox, True, True, 0)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
hbox.pack_start(vbox, True, True, 0)
label1 = Gtk.Label(label="Automatic Date & Time", xalign=0)
label2 = Gtk.Label(label="Requires internet access", xalign=0)
vbox.pack_start(label1, True, True, 0)
vbox.pack_start(label2, True, True, 0)
switch = Gtk.Switch()
switch.props.valign = Gtk.Align.CENTER
hbox.pack_start(switch, False, True, 0)
listbox.add(row)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
label = Gtk.Label(label="Enable Automatic Update", xalign=0)
check = Gtk.CheckButton()
hbox.pack_start(label, True, True, 0)
hbox.pack_start(check, False, True, 0)
listbox.add(row)
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
row.add(hbox)
label = Gtk.Label(label="Date Format", xalign=0)
combo = Gtk.ComboBoxText()
combo.insert(0, "0", "24-hour")
combo.insert(1, "1", "AM/PM")
hbox.pack_start(label, True, True, 0)
hbox.pack_start(combo, False, True, 0)
listbox.add(row)
listbox_2 = Gtk.ListBox()
items = "This is a sorted ListBox Fail".split()
for item in items:
listbox_2.add(ListBoxRowWithData(item))
def sort_func(row_1, row_2, data, notify_destroy):
return row_1.data.lower() > row_2.data.lower()
def filter_func(row, data, notify_destroy):
return False if row.data == "Fail" else True
listbox_2.set_sort_func(sort_func, None, False)
listbox_2.set_filter_func(filter_func, None, False)
def on_row_activated(listbox_widget, row):
print(row.data)
listbox_2.connect("row-activated", on_row_activated)
box_outer.pack_start(listbox_2, True, True, 0)
listbox_2.show_all()
win = ListBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
6.4. Stack och StackSwitcher¶
En Gtk.Stack
är en behållare som bara visar ett av sina barn åt gången. Till skillnad från Gtk.Notebook
så tillhandahåller Gtk.Stack
inte något sätt för användare att ändra det synliga barnet. Istället kan komponenten Gtk.StackSwitcher
användas med Gtk.Stack
för att tillhandahålla denna funktionalitet.
Övergångar mellan sidor kan animeras som bildspel eller toningar. Detta kan styras med Gtk.Stack.set_transition_type()
. Dessa animeringar följer inställningen ”gtk-enable-animations”.
Övergångshastighet kan justeras med Gtk.Stack.set_transition_duration()
Komponenten Gtk.StackSwitcher
agerar som en styrenhet för en Gtk.Stack
; den visar en knapprad för att växla mellan de olika sidorna för den associerade stackkomponenten.
Allt innehåll för knapparna kommer från barnegenskaperna för Gtk.Stack
.
Det är möjligt att associera flera Gtk.StackSwitcher
-komponenter med samma Gtk.Stack
-komponent.
6.4.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class StackWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Stack Demo")
self.set_border_width(10)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(vbox)
stack = Gtk.Stack()
stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
stack.set_transition_duration(1000)
checkbutton = Gtk.CheckButton(label="Click me!")
stack.add_titled(checkbutton, "check", "Check Button")
label = Gtk.Label()
label.set_markup("<big>A fancy label</big>")
stack.add_titled(label, "label", "A label")
stack_switcher = Gtk.StackSwitcher()
stack_switcher.set_stack(stack)
vbox.pack_start(stack_switcher, True, True, 0)
vbox.pack_start(stack, True, True, 0)
win = StackWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
6.5. HeaderBar¶
En Gtk.HeaderBar
liknar en horisontell Gtk.Box
, den tillåter att barn placeras i början eller slutet. Dessutom tillåter den att en titel visas. Titeln kommer centreras med avseende på rutans bredd, även om barnen på endera sida tar upp olika mycket utrymme.
Eftersom GTK+ nu stöder dekoration på klientsidan (CSD) så kan en Gtk.HeaderBar
användas istället för namnlisten (vilken renderas av fönsterhanteraren).
En Gtk.HeaderBar
placeras vanligen högst upp i ett fönster och bör innehålla vanligen använda kontroller som påverkar innehållet nedan. De tillhandahåller också åtkomst till fönsterstyrkomponenter, vilket inkluderar knappen för att stänga fönstret och fönstermenyn.
6.5.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio
class HeaderBarWindow(Gtk.Window):
def __init__(self):
super().__init__(title="HeaderBar Demo")
self.set_border_width(10)
self.set_default_size(400, 200)
hb = Gtk.HeaderBar()
hb.set_show_close_button(True)
hb.props.title = "HeaderBar example"
self.set_titlebar(hb)
button = Gtk.Button()
icon = Gio.ThemedIcon(name="mail-send-receive-symbolic")
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
button.add(image)
hb.pack_end(button)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
Gtk.StyleContext.add_class(box.get_style_context(), "linked")
button = Gtk.Button()
button.add(
Gtk.Arrow(arrow_type=Gtk.ArrowType.LEFT, shadow_type=Gtk.ShadowType.NONE)
)
box.add(button)
button = Gtk.Button.new_from_icon_name("pan-end-symbolic", Gtk.IconSize.MENU)
box.add(button)
hb.pack_start(box)
self.add(Gtk.TextView())
win = HeaderBarWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
6.6. FlowBox¶
Observera
Detta exempel kräver åtminstone GTK+ 3.12.
En Gtk.FlowBox
är en behållare som positionerar barnkomponenter i ordning enligt sin orientering.
Exempelvis kommer komponenterna vid horisontell orientering att arrangeras från vänster till höger, och starta en ny rad under föregående rad då detta är nödvändigt. Att minska bredden kommer i detta fall kräva fler rader, så en större höjd kommer begäras.
På samma sätt kommer komponenterna vid vertikal orientering att arrangeras uppifrån och ner, och starta en ny kolumn till höger då detta är nödvändigt. Att minska höjden kommer kräva fler kolumner, så en större bredd kommer begäras.
Barnen till en Gtk.FlowBox
kan sorteras och filtreras dynamiskt.
Även om en Gtk.FlowBox
endast får ha barn som är Gtk.FlowBoxChild
, så kan du lägga till valfri komponent till den med Gtk.Container.add()
, så kommer automatiskt en Gtk.FlowBoxChild
-komponent infogas mellan rutan och komponenten.
6.6.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk
class FlowBoxWindow(Gtk.Window):
def __init__(self):
super().__init__(title="FlowBox Demo")
self.set_border_width(10)
self.set_default_size(300, 250)
header = Gtk.HeaderBar(title="Flow Box")
header.set_subtitle("Sample FlowBox app")
header.props.show_close_button = True
self.set_titlebar(header)
scrolled = Gtk.ScrolledWindow()
scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
flowbox = Gtk.FlowBox()
flowbox.set_valign(Gtk.Align.START)
flowbox.set_max_children_per_line(30)
flowbox.set_selection_mode(Gtk.SelectionMode.NONE)
self.create_flowbox(flowbox)
scrolled.add(flowbox)
self.add(scrolled)
self.show_all()
def on_draw(self, widget, cr, data):
context = widget.get_style_context()
width = widget.get_allocated_width()
height = widget.get_allocated_height()
Gtk.render_background(context, cr, 0, 0, width, height)
r, g, b, a = data["color"]
cr.set_source_rgba(r, g, b, a)
cr.rectangle(0, 0, width, height)
cr.fill()
def color_swatch_new(self, str_color):
rgba = Gdk.RGBA()
rgba.parse(str_color)
button = Gtk.Button()
area = Gtk.DrawingArea()
area.set_size_request(24, 24)
area.connect("draw", self.on_draw, {"color": rgba})
button.add(area)
return button
def create_flowbox(self, flowbox):
colors = [
"AliceBlue",
"AntiqueWhite",
"AntiqueWhite1",
"AntiqueWhite2",
"AntiqueWhite3",
"AntiqueWhite4",
"aqua",
"aquamarine",
"aquamarine1",
"aquamarine2",
"aquamarine3",
"aquamarine4",
"azure",
"azure1",
"azure2",
"azure3",
"azure4",
"beige",
"bisque",
"bisque1",
"bisque2",
"bisque3",
"bisque4",
"black",
"BlanchedAlmond",
"blue",
"blue1",
"blue2",
"blue3",
"blue4",
"BlueViolet",
"brown",
"brown1",
"brown2",
"brown3",
"brown4",
"burlywood",
"burlywood1",
"burlywood2",
"burlywood3",
"burlywood4",
"CadetBlue",
"CadetBlue1",
"CadetBlue2",
"CadetBlue3",
"CadetBlue4",
"chartreuse",
"chartreuse1",
"chartreuse2",
"chartreuse3",
"chartreuse4",
"chocolate",
"chocolate1",
"chocolate2",
"chocolate3",
"chocolate4",
"coral",
"coral1",
"coral2",
"coral3",
"coral4",
]
for color in colors:
button = self.color_swatch_new(color)
flowbox.add(button)
win = FlowBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|
6.7. Notebook¶
Komponenten Gtk.Notebook
är en Gtk.Container
vars barn är sidor som kan växlas mellan med fliketiketter längs en kant.
Det finns många konfigurationsalternativ för GtkNotebook. Bland annat kan du välja på vilken kant flikarna visas (se Gtk.Notebook.set_tab_pos()
), huruvida anteckningsboken ska göras större eller om rullningspilar ska läggas till då det finns för många flikar för att rymmas (se Gtk.Notebook.set_scrollable()
), och huruvida det ska finnas en poppuppmeny som låter användarna växla sidor (se Gtk.Notebook.popup_enable()
, Gtk.Notebook.popup_disable()
).
6.7.1. Exempel¶

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
super().__init__(title="Simple Notebook Example")
self.set_border_width(3)
self.notebook = Gtk.Notebook()
self.add(self.notebook)
self.page1 = Gtk.Box()
self.page1.set_border_width(10)
self.page1.add(Gtk.Label(label="Default Page!"))
self.notebook.append_page(self.page1, Gtk.Label(label="Plain Title"))
self.page2 = Gtk.Box()
self.page2.set_border_width(10)
self.page2.add(Gtk.Label(label="A page with an image for a Title."))
self.notebook.append_page(
self.page2, Gtk.Image.new_from_icon_name("help-about", Gtk.IconSize.MENU)
)
win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
|