Skip to content

gtk-rs

Tasks

Development environment

Red Hat
dnf install gtk4-devel gcc
Ubuntu
apt install libgtk-4-dev build-essential

Boilerplate

Interface
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.40"/>
<object class="GtkApplicationWindow" id="window">
    <property name="title">My GTK App</property>
    <property name="default-width">300</property>
    <property name="default-height">300</property>
</object>
</interface>
use gtk4::{Application, ApplicationWindow};

fn main() {
    let app = Application::builder()
        .application_id("com.example.learning-gtk")
        .build();

    app.connect_activate(build_ui);
    app.run();
}

fn build_ui(app: &Application) {
    let window = ApplicationWindow::builder()
        .application(app)
        .default_width(300)
        .default_height(300)
        .title("My GTK App")
        .build();

    window.present();
}

Clicker

This appears to be a common demonstration of data binding in various GUI frameworks, the code below is taken from here.

use gtk4::prelude::*;
use gtk4::{glib, Application, ApplicationWindow, Box, Button, Orientation};
use std::{cell::Cell, rc::Rc};

fn main() {
    // Create a new application
    let app = Application::builder()
        .application_id("org.gtk-rs.example")
        .build();

    // Connect to "activate" signal of `app`
    app.connect_activate(build_ui);

    // Run the application
    app.run();
}

fn build_ui(app: &Application) {
    // Create two buttons
    let button_increase = Button::builder()
        .label("Increase")
        .margin_top(12)
        .margin_bottom(12)
        .margin_start(12)
        .margin_end(12)
        .build();
    let button_decrease = Button::builder()
        .label("Decrease")
        .margin_top(12)
        .margin_bottom(12)
        .margin_start(12)
        .margin_end(12)
        .build();

    // Reference-counted object with inner mutability
    let number = Rc::new(Cell::new(0)); // (1)

    // Connect callbacks
    // When a button is clicked, `number` and label of the other button will be changed
    button_increase.connect_clicked(glib::clone!(@weak number, @weak button_decrease => // (2)
        move |_| {
            number.set(number.get() + 1);
            button_decrease.set_label(&number.get().to_string());
    }));
    button_decrease.connect_clicked(glib::clone!(@weak button_increase => // (3)
        move |_| {
            number.set(number.get() - 1);
            button_increase.set_label(&number.get().to_string());
    }));

    // Add buttons to `gtk_box`
    let gtk_box = Box::builder().orientation(Orientation::Vertical).build();
    gtk_box.append(&button_increase);
    gtk_box.append(&button_decrease);

    // Create a window
    let window = ApplicationWindow::builder()
        .application(app)
        .title("My GTK App")
        .child(&gtk_box)
        .build();

    // Present the window
    window.present();
}
  1. It appears number must be an Rc<Cell>. Changing it to a raw i8 causes compilation errors within glib::clone.
  2. Removing the weak reference to number causes a compilation error.
  3. Adding a weak reference to number here also causes a compilation error.

Hello, World!

Window frame

TODO

At the moment, this example is broken because I don't know how to pass the string into the Application struct for string interpolation.

use gtk4::prelude::*;
use gtk4::{Application, ApplicationWindow};

fn main() {
    let name = String::new();
    if let Some(s) = 42std::env::args().nth(1) {
        name = s;
    } else {
        name = String::from("World");
    };

    let app = Application::builder()
        .application_id("com.example.learning-gtk")
        .build();
    app.connect_activate(build_ui);
    println!("{}", app.name);
    app.run();
}

fn build_ui(app: &Application) {
    let window = ApplicationWindow::builder()
        .application(app)
        .title("Hello, World!")
        .default_height(300)
        .default_width(300)
        .build();
    window.present();
}

Button reveal

Interface
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="3.40">
  <object class="GtkApplicationWindow" id="window">
    <property name="title">My GTK App</property>
    <property name="default-width">300</property>
    <property name="default-height">300</property>
      <child>
        <object class="GtkButton" id="button">
          <property name="label">Press me!</property>
          <property name="margin-top">12</property>
          <property name="margin-bottom">12</property>
          <property name="margin-start">12</property>
          <property name="margin-end">12</property>  
        </object>
      </child>
    </object>
</interface>
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button};

fn main() {
    let app = Application::builder()
        .application_id("org.gtk-rs.example")
        .build();

    app.connect_activate(build_ui);
    app.run();
}

fn build_ui(app: &Application) {
    let builder = gtk::Builder::from_string(include_str!("window.ui"));
    let window: ApplicationWindow = builder
        .object("window")
        .expect("Could not get object `window` from builder.");
    let button: Button = builder
        .object("button")
        .expect("Could not get object `button` from builder.");

    window.set_application(Some(app));

    button.connect_clicked(move |button| { // (1)
        button.set_label("Hello World!");
    });

    window.set_child(Some(&button));
    window.show_all();
    window.present();
}
  1. This move keyword appears to be unnecessary.

API

glib::clone

glib::clone! is used to create closures using strong or weak references.

use glib;
use glib_macros::clone;
use std::rc::Rc;

let v = Rc::new(1);
let closure = clone!(@strong v => move |x| {
    println!("v: {}, x: {}", v, x);
});

closure(2);
use glib;
use glib_macros::clone;
use std::rc::Rc;

let u = Rc::new(2);
let closure = clone!(@weak u => move |x| {
    println!("u: {}, x: {}", u, x);
});

closure(3);

glib::wrapper

glib::wrapper is used to wrap GObjects and figures prominently in the To-Do App. Parent classes must be provided after @extends and any interfaces implemented must be provided after @implements

wrapper! {
    pub struct $name($kind<$foreign>); // (2)

    match fn {
        $fn_name => /* (1) */,
        ...
    }
}
  1. Closure-like expressions in the match fn block allow copying, freeing, referencing, and dereferencing the value that is being wrapped.
  2. There are three possible values for $kind: Boxed (heap allocated types), Shared (records with reference-counted, shared ownership), or Object (classes)
wrapper! {
    pub struct Button(Object<ffi::GtkButton>)
        @extends Bin, Container, Widget,
        @implements Buildable, Actionable;

    match fn {
        type_ => || ffi::gtk_button_get_type(), // (1)
    }
}

Action

Gio.Action is a way to expose any single task an application or widget does by a name. Classes like Gio.MenuItem and Gtk.ModelButton support properties to set an action name. These actions can be collected into a Gio.ActionGroup.

ActionGroup

Adjustment

PyGobject gtk-rs gtk

Gtk.Adjustment is not a widget per se but is used in many widgets, including spin buttons, view ports, and children of Gtk.Range.

  • page increment and page size refer to actions taken when the user presses PgUp or PgDn
    Gtk.Adjustment.new(initial_value, lower_range, upper_range, step_increment, page_increment, page_size)
    

Alignment

PyGobject gtk-rs gtk

Gtk.Alignment controls the alignment and size of its child widget.

Application

PyGobject gtk-rs gtk
Gtk.Application gtk4::Application GtkApplication

Subclasses of Gtk.Application encapsulate application behavior, including application startup and CLI processing. In practice it is simply a wrapper for the ApplicationWindow class which is instantiated in the do_activate() hook. Notably, the Application subclass provides the value for the application_id kwarg passed to the Gtk.Application constructor. This value is validated, and any simple string is not silently accepted.

Application must expose several important methods:

  • do_activate()
    def do_activate(self):
    self.window = ApplicationWindow(application=self, title="Hello, World!")
    self.window.show_all()
    self.window.present()
    
  • do_startup()

ApplicationWindow

PyGobject gtk-rs gtk
Gtk.ApplicationWindow gtk4::ApplicationWindow GtkApplicationWindow

The Gtk.ApplicationWindow class is the main visible window for the application, and the only window for "single-instance" applications (which is the default). The ApplicationWindow class was introduced in GTK 3.4.

When an action has the prefix win. it specifies that the ApplicationWindow subclass will process the signal.

Assistant

PyGobject gtk-rs gtk
Gtk.Assistant gtk4::Assistant GtkAssistant

Gtk.Assistant widgets are used to implement the wizard pattern.

Box

PyGobject gtk-rs gtk
Gtk.Box gtk4::Box GtkBox

Builder

PyGobject gtk-rs gtk
Gtk.Builder gtk4::Builder GtkBuilder

Gtk.Builder allows the use of interfaces to define widget layouts. Individual UI elements can be bound if they have an id attribute assigned.

fn main() {
    let app = gtk::Application::builder()
        .application_id("org.example.gtk-app")
        .build();

    app.connect_activate(build_ui);
    app.run();
}

fn build_ui(app: &Application):{
    let builder = gtk::Builder::from_string(include_str!("window.ui"));
    let window: ApplicationWindow = builder.object("window")
        .expect("Error loading ApplicationWindow!");

    window.set_application(Some(app));
    window.show_all();
    window.present();
}
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(application_id = "org.example.gtk-app")

    def do_activate(self):
        builder = Gtk.Builder.new_from_file("window.ui")
        self.window = builder.get_object("window")
        self.window.show_all()
        self.window.present()

    def run(self):
        super().run()
        Gtk.main()

CheckButton

PyGobject gtk-rs gtk
Gtk.CheckButton gtk4::CheckButton GtkCheckButton

Gtk.CheckButtons include checkboxes and (when placed into groups) radio buttons.

Container

PyGobject gtk-rs gtk
Gtk.Container gtk::Container GtkContainer (3.0)

Both Gtk.ApplicationWindow and Gtk.Window classes indirectly derive from the abstract class Gtk.Container. The main purpose of a container subclass is to allow a parent widget to contain one or more child widgets, and there are two types:

Dialog

PyGobject gtk-rs gtk

Gtk.Dialog provides a convenient way to prompt the user for a small amount of input. It is a widget that can be instantiated and customized in its own right as well as a parent to various subclasses.

dialog = Gtk.Dialog(title="Hello, World!", parent=parent)

Dialogs are split into two parts:

  • Content area containing interactive widgets
  • Action area containing buttons

These areas are both combined in a vertical Box that is assigned to the vbox field. The action area is packed to the end of this vbox, so the pack_start() method is used to add widgets to the content area.

Dialog boxes can be modal, meaning they prevent interaction with the main window while open, or nonmodal.

dialog = Gtk.Dialog(title="Hello, World!", parent=parent, modal=True)
dialog = Gtk.Dialog(title="Hello, World!", parent=parent, modal=False)

Gtk.MessageDialog is a subtype of Dialog meant to simplify the process of creating simple dialogs.

Buttons are added procedurally using add_button(), passing a display string (with support for mnemonics using _) and a ResponseType enum (they once could be added on instantiation by passing a tuple to the buttons keyword argument).

dialog.add_button("_OK", Gtk.ResponseType.OK)

Methods:

  • add_button()

Entry

PyGobject gtk-rs gtk

Unlike other widgets, Gtk.Entry can be instantiated without using a specific constructor.

entry = Gtk.Entry()

Default text can be provided by passing a string to the text keyword argument or with the set_text() setter method:

entry = Gtk.Entry(text="Hello, World!")
entry.set_text("Hello, World!")

A password field can be made by concealing text by passing False to visibility or with the set_visibility() setter:

password = Gtk.Entry(visibility=False)
password.set_visibility(False)
  • get_text() retrieve contents (string)
  • set_visibility(bool) conceal text

EventBox

PyGobject gtk-rs gtk

Gtk.EventBox is a container widget that allows event handling for widgets like Gtk.Label that do not have an associated GDK window. The event box can be positioned above or below the windows of its child with set_above_child() (False by default.) An EventBox must also have a Gtk.EventMask enum set to specify the type of events the widget may receive. This enum is passed as a value to set_events().

In the following example, an event handler is connected to the EventBox to handle button_press_event. This event handler changes the text of the Label after a double-click.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk


class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        self.set_size_request(200, 50)
        eventbox = Gtk.EventBox.new()
        label = Gtk.Label.new("Double-Click Me!")
        eventbox.set_above_child(False)
        eventbox.connect("button_press_event", self.on_button_pressed, label)
        eventbox.add(label)
        self.add(eventbox)
        eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        eventbox.realize()

    def on_button_pressed(self, eventbox, event, label):
        if event.type == Gdk.EventType._2BUTTON_PRESS:
            text = label.get_text()
            if text[0] == 'D':
                label.set_text("I Was Double-Clicked!")
            else:
                label.set_text("Double-Click Me Again!")
        return False

class Application(Gtk.Application):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp", **kwargs)
        self.window = None

    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Hello World!")
        self.window.show_all()
        self.window.present()

if __name__ == "__main__":
    app = Application()
    app.run()

FileChooserDialog

PyGobject gtk-rs gtk

Gtk.FileChooserDialog is one of the important subtypes of Gtk.Dialog. Like other dialogs, it is provided a title and parent window on instantiation. Additionally a FileChooserAction enum must be specified.

FileChooserActions include:

  • Gtk.FileChooserAction.SAVE
  • Gtk.FileChooserAction.OPEN
  • Gtk.FileChooserAction.SELECT_FOLDER
  • Gtk.FileChooserAction.CREATE_FOLDER
dialog = Gtk.FileChooserDialog(
    title="Save file as ...",
    parent=parent,
    action=FileChooserAction.SAVE
)

Selected files are then retrieved using

dialog.get_filenames()

| Setter | Property | Description | | --------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | set_current_folder | | Specify directory in filesystem where FileChooser will start | | set_current_name | | For FileChooserAction.SAVE, suggest a filename | | set_select_multiple | select_multiple | For FileChooserAction.OPEN or SELECT_FOLDER, allow multiple file or folder selections |

Grid

PyGobject gtk-rs gtk

Gtk.Grid allows children to be packed in a two-dimensional grid. Grids are instantiated with new() and widgets are laid out by calling attach() (see Login for an example).

  • attach() lay out a widget providing column and row numbers followed by column and row spans
    grid.attach(label, 0, 0, 1, 1)
    

HeaderBar

PyGobject gtk-rs gtk

Gtk.HeaderBar allows the titlebar to be customized. Like other widgets, it can be configured on instantiation by providing values to keyword arguments or by using setters.

Adding to a window

HeaderBars are added with set_titlebar(). This is unlike other widgets which are assigned to an ApplicationWindow or Window using pack_start(), pack_end(), or add(),

class ApplicationWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        headerbar = Gtk.HeaderBar(title=f"Hello, World!", 
                                subtitle="HeaderBar example", 
                                show_close_button=True)
        self.set_titlebar(headerbar)
class ApplicationWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        headerbar = Gtk.HeaderBar()
        headerbar.set_title(f"Hello, World!")
        headerbar.set_subtitle("HeaderBar example")
        headerbar.set_show_close_button(True)
        self.set_titlebar(headerbar)

Label

PyGobject gtk-rs gtk

Note that Gtk.Label sets its text with "label" and not "text" as you may expect from the corresponding setter.

label = Gtk.Label(label="Hello, World!")
label.set_text("Hello, World!")

ListBox

PyGobject gtk-rs gtk

Gtk.ListBox is a vertical container of Gtk.ListBoxRow children used as an alternative to TreeView when the children need to be interactive, as in a list of settings. ListBox

ListStore

PyGobject gtk-rs gtk

Gtk.ListStore is one of the two major classes that serves as combination schema and database backing Gtk.TreeView, the other being Gtk.TreeStore.

It is instantiated with a sequence of data types, similar to a database schema. These can be standard Python types or GObjects (which are mapped to the Python types anyway):

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

liststore = Gtk.ListStore((str, int, str))
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GObject

liststore = Gtk.ListStore((GObject.TYPE_STRING, GOBject).TYPE_INT, GObject.TYPE_STRING))

This object then exposes an append method which is used to add records:

liststore.append(["Socrates", 350, "Athens"])

The store is then associated with the treeview with set_model

treeview.set_model(liststore)

PyGobject gtk-rs gtk

Gtk.MenuBar is populated with Gtk.MenuItems, corresponding to the expandable menu items (i.e. "File", "Edit", and "Help"). Gtk.Menu is actually used for the submenu, which like MenuBar is also populared with MenuItems. A Menu is attached to the MenuItem of a MenuBar by using the set_submenu() method on the Menu object. This setter does not have a corresponding kwarg, so all menus have to be constructed procedurally.

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

class AppWindow(Gtk.ApplicationWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_size_request(250, -1)
        menubar = Gtk.MenuBar.new()
        self.add(menubar)

        file = Gtk.MenuItem.new_with_label("File")
        menubar.append(file)
        filemenu = Gtk.Menu.new()
        file.set_submenu(filemenu)
        new = Gtk.MenuItem.new_with_label("New")
        open = Gtk.MenuItem.new_with_label("Open")
        filemenu.append(new)
        filemenu.append(open)

        edit = Gtk.MenuItem.new_with_label("Edit")
        menubar.append(edit)
        editmenu = Gtk.Menu.new()
        edit.set_submenu(editmenu)
        cut = Gtk.MenuItem.new_with_label("Cut")
        copy = Gtk.MenuItem.new_with_label("Copy")
        paste = Gtk.MenuItem.new_with_label("Paste")
        editmenu.append(cut)
        editmenu.append(copy) 
        editmenu.append(paste)

        help = Gtk.MenuItem.new_with_label("Help")
        menubar.append(help)
        helpmenu = Gtk.Menu.new()
        help.set_submenu(helpmenu)
        contents = Gtk.MenuItem.new_with_label("Help")
        about = Gtk.MenuItem.new_with_label("About")
        helpmenu.append(contents)
        helpmenu.append(about)

class Application(Gtk.Application):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp", **kwargs)
        self.window = None

    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Menu Bars")
        self.window.show_all()
        self.window.present()

if __name__ == "__main__":
    app = Application()
    app.run()

Notebook

PyGobject gtk-rs gtk

Gtk.Notebook is a layout container that organizes content into tabbed pages. It is instantiated with the new() method and pages are appended with the append_page() method, passing content and label widgets as arguments.

The tab bar can be placed using set_tab_pos(), passing a Gtk.PositionType enum

notebook = Gtk.Notebook.new()
# notebook.set_tab_pos(Gtk.PositionType.TOP)

notebook = Gtk.Notebook.new()
notebook.set_tab_pos(Gtk.PositionType.RIGHT)

notebook = Gtk.Notebook.new()
notebook.set_tab_pos(Gtk.PositionType.BOTTOM)

notebook = Gtk.Notebook.new()
notebook.set_tab_pos(Gtk.PositionType.LEFT)

notebook = Gtk.Notebook.new()
label = Gtk.Label.new("Tab title")
child = Gtk.Label.new("Tab content")
notebook.append_page(child, label)

The label widget is commonly Gtk.Label but can also be a Gtk.Box.

The tab bar can be made scrollable using set_scrollable(), passing a bool.

Scale

PyGobject gtk-rs gtk

Gtk.Scale widgets are sliders, and they can be instantiated in one of two ways:

  • new() passing an Adjustment object
  • new_with_range(min, max, step) passing values for minimum, maximum, and step

Scale values are stored as doubles, so integers have to be simulated by reducing the number of digits to 0 using set_digits(). By default, the number of digits is set to that of the step value.

ScrolledWindow

PyGobject gtk-rs gtk

Gtk.ScrolledWindow is a decorator container that accepts a single child widget. Widgets that implement the Gtk.Scrollable interface have native scrolling suppport, like Gtk.TreeView, Gtk.TextView, and Gtk.Layout. Other widgets have to use Gtk.Viewport as an adaptor, and must be added to a Viewport which is then added to the ScrolledWindow.

It is instantiated with the new() method, optionally passing two Adjustment objects that affect horizontal and vertical scrolling behavior when stepping or paging.

scrolled_win = Gtk.ScrolledWindow.new(None,None)

Statusbar

PyGobject gtk-rs gtk
Gtk.Statusbar gtk4::Statusbar GtkStatusbar (4.0)

Gtk.Statusbar (note the lowercase b ) stores a stack of messages, the topmost of which is displayed. Before adding messages, a context identifier, a unique unsigned integer associated with a context description string, must be retrieved from the newly created Statusbar by passing a string value to get_context_id(). This allows messages to be categorized and pushed to separate stacks.

statusbar.push(context_id, message)

Switch

PyGobject gtk-rs gtk
Gtk.Switch gtk4::Switch GtkSwitch (4.0)

Gtk.Switch allows a user to toggle a boolean value. Switch exposes getters and setters for both state (which is represented by the trough color) and active (switch position) properties. State is the backend to activ, and they are kept in sync.

import gi

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


class ApplicationWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)

        box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        listbox = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
        row = Gtk.ListBoxRow()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
        label1 = Gtk.Label(label="Automatic Date & Time", xalign=0)

        hbox.add(label1)
        self.switch = Gtk.Switch(valign=Gtk.Align.CENTER, state=False)
        hbox.add(self.switch)
        row.add(hbox)
        listbox.add(row)
        box_outer.add(listbox)
        self.add(box_outer)
        button = Gtk.Button(label="Click")
        button.connect("clicked", self.on_button_clicked)
        box_outer.add(button)

    def on_button_clicked(self, button):
        print(f"Value of get_active(): {self.switch.get_active()}")
        print(f"Value of get_state(): {self.switch.get_state()}")


class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.window = None

    def do_activate(self):
        if not self.window:
            self.window=ApplicationWindow(application=self)
        self.window.show_all()
        self.window.present()

if __name__ == "__main__":
    app = Application()
    app.run()

TreeView

PyGobject gtk-rs gtk
Gtk.TreeView gtk4::TreeView GtkTreeView (4.0) GtkTreeView (3.0)

In order to create a tree or list in GTK, the Gtk.TreeView widget is paired with a Gtk.TreeModel interface, the most typical implementation of which is Gtk.ListStore or Gtk.TreeStore. TreeView is a complicated widget that must be constructed procedurally:

  1. Gtk.TreeView is instantiated. A ListStore is specified as data model and passed in as the value of the model kwarg. The ListStore specifies the schema of the data as a collection of types.
    treeview = Gtk.TreeView(model=Gtk.ListStore.new((str)))
    
    Alternatively, the ListStore can be specified after instantiation.
    treeview = Gtk.TreeView.new()
    treeview.set_model(Gtk.ListStore.new([str]))
    
  2. A Gtk.TreeViewColumn is created for every column in the model. These require a Gtk.CellRenderer to be defined. The TreeViewColumn is added to the treeview by calling the append_column() method on the treeview. The text kwarg appears to refer to the column of the data store to use for the column's values.
    treeview.append_column(Gtk.TreeViewColumn("Greeks", Gtk.CellRendererText.new(), text=0))
    
  3. Items are added to the ListStore procedurally using the append() method. Note that the method takes only a single argument, so collections like lists or tuples must be used.
    liststore.append(("Socrates",))
    liststore.append(("Plato",))
    liststore.append(("Aristotle",))
    

Changing the number of columns affects the types used to define the ListStore, the appended records, as well as the number of columns added to the TreeView itself.

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


class ApplicationWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.gen_treeview()

        scrolled_win = Gtk.ScrolledWindow.new(None,None)
        scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrolled_win.add(self.treeview)

        self.add(scrolled_win)
        self.set_size_request(200,200)

    def get_liststore(self):
        store = Gtk.ListStore.new((str,))
        store.append(("Socrates",))
        store.append(("Plato",))
        store.append(("Aristotle",))
        return store

    def gen_treeview(self):
        self.treeview = Gtk.TreeView.new()
        self.treeview.set_model(self.get_liststore())
        self.treeview.append_column(Gtk.TreeViewColumn("Greeks", Gtk.CellRendererText.new(), text=0))



class Application(Gtk.Application):
    def __init__(self):
        super().__init__(application_id='org.example.myapp')

    def do_activate(self):
        self.window = ApplicationWindow(application=self, title="Greeks")
        self.window.show_all()
        self.window.present()


if __name__ == '__main__':
    app = Application()
    app.run()
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk


class ApplicationWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.gen_treeview()

        scrolled_win = Gtk.ScrolledWindow.new(None,None)
        scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        scrolled_win.add(self.treeview)

        self.add(scrolled_win)
        self.set_size_request(200,200)

    def get_liststore(self):
        store = Gtk.ListStore.new((str, str))
        store.append(["Socrates", "Athens"])
        store.append(["Plato", "Athens"])
        store.append(["Aristotle", "Athens"])
        return store

    def gen_treeview(self):
        self.treeview = Gtk.TreeView.new()
        self.treeview.set_model(self.get_liststore())
        self.treeview.append_column(Gtk.TreeViewColumn("Greeks", Gtk.CellRendererText.new(), text=0))
        self.treeview.append_column(Gtk.TreeViewColumn("Place of birth", Gtk.CellRendererText.new(), text=1))


class Application(Gtk.Application):
    def __init__(self):
        super().__init__(application_id='org.example.myapp')

    def do_activate(self):
        self.window = ApplicationWindow(application=self, title="Greeks")
        self.window.show_all()
        self.window.present()


if __name__ == '__main__':
    app = Application()
    app.run()

The model backing a TreeView (usually a ListStore), can be retrieved with the get_model() method.

treeview.get_model().append(('foo','bar'))

TreeView emits several signals:

  • row_activated when a row is double-clicked, with the following implicit argument
    • widget refering to the emitting TreeView widget itself
    • path is a TreePath.
    • column is of type TreeViewcolumn
      treeview.connect("row_activated", self.on_row_activated, widget, path, column)
      
      def on_row_activated(self, widget, path, column):
          row = path.get_indices()[0]
          print(f"row={path.get_indices()[0]},col={column.props.title}")
          print(widget.get_model()[row][:])
      

TreePath

PyGobject gtk-rs gtk

Gtk.TreePath is a type used to implement the rows of a TreeView. Although it prints to an integer with the print statement, it cannot be treated as one.

A path object can be passed as the index to a TreeModel like ListStore, as can an integer. The row number of a TreePath from a normal list-style TreeView can be retrieved with the get_indices() method.

row = path.get_indices()[0]

# Using TreePath object as index to model
model[path][:]

# Using row integer as index to model
model[row][:]

Another method on TreePath, get_depth() always returns 1 for list-style TreeViews, but may be more useful for tree-style TreeViews.

TreeSelection

PyGobject gtk-rs gtk

Gtk.TreeSelection objects represent selection information for each tree view.

TreeViewColumn

PyGobject gtk-rs gtk
Gtk.TreekViewColumn gtk4::TreeViewColumn GtkTreeViewColumn (4.0) GtkTreeViewColumn (3.0)

Gtk.TreeViewColumn represents a visible column in a Treeview. Its props property exposes many associated values, including title.

print(column.props.title)
A column is made sortable by calling set_sort_column_id(), passing the column of the model to sort by.
column.set_sort_column_id(0)

Window

PyGobject gtk-rs gtk
Gtk.Window gtk4::Window GtkWindow (4.0) GtkWindow (3.0)