Search code examples
pythongtkgtk3pygtkpygobject

PyGObject page switching with buttons


I want to switch pages with the help of buttons in Gtk.Stack. There are 3 pages, and the title bar of the application has one forward and one back button. I want it to go to the next page when the forward button is pressed, and to go to the previous page when the back button is pressed. Its current state can only switch between page 1 and page 2.

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

Handy.init()

class MyWindow(Handy.Window):
    def __init__(self):
        super().__init__(title="Hello World")
        self.set_default_size(500, 300)

        # WindowHandle
        self.handle = Handy.WindowHandle()
        self.add(self.handle)

        # Box
        self.winbox = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
        self.handle.add(self.winbox)

        # Headerbar
        self.hb = Handy.HeaderBar()
        self.hb.set_show_close_button(True)
        self.hb.props.title = "Stack Example"
        self.winbox.pack_start(self.hb, False, True, 0)

        # Stack
        self.stack = Gtk.Stack()
        self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
        self.winbox.pack_start(self.stack, True, True, 0)

        # Labels
        self.label = Gtk.Label(label="Page 1")
        self.stack.add_titled(self.label, "page0", "Label")

        self.label = Gtk.Label(label="Page 2")
        self.stack.add_titled(self.label, "page1", "Label")

        self.label = Gtk.Label(label="Page 3")
        self.stack.add_titled(self.label, "page2", "Label")

        # Headerbar button 1
        self.button = Gtk.Button()
        self.button = Gtk.Button.new_from_icon_name("pan-start-symbolic", Gtk.IconSize.MENU)
        self.hb.pack_start(self.button)
        self.button.connect('clicked', self.on_button1_clicked)

        # Headerbar button 2
        self.button2 = Gtk.Button()
        self.button2 = Gtk.Button.new_from_icon_name("pan-end-symbolic", Gtk.IconSize.MENU)
        self.hb.pack_start(self.button2)
        self.button2.connect("clicked", self.on_button2_clicked)

    def on_button1_clicked(self, widget):
        self.stack.set_visible_child_name("page1")
    def on_button2_clicked(self, widget):
        self.stack.set_visible_child_name("page2")


win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

app


Solution

  • I don't know if there's an easy way to make visible the next child in a GtkStack or if another container has this functionality apart from GtkAssistant.

    Nevertheless there's multiple ways you can implement this yourself. Either like so:

    def on_button1_clicked(self, widget):
        pages = self.stack.get_children()
        cur_page = self.stack.get_visible_child()
        i = pages.index(cur_page)
        if i == 0: return
        self.stack.set_visible_child(pages[i-1])
    
    def on_button2_clicked(self, widget):
        pages = self.stack.get_children()
        cur_page = self.stack.get_visible_child()
        i = pages.index(cur_page)
        if i == len(pages) - 1: return
        self.stack.set_visible_child(pages[i+1])
    

    where you get the stack's children with GtkContainer.get_children(), find the index of the current visible child and then plus/minus one to get the next/prev page. Caveat: I'm not sure if get_children() always returns the child widgets in the order they are added.

    Alternatively in your __init__() function, you can create a list to store your page names/widgets e.g. self.page_names = ['page0', 'page1', 'page2']. And then you can do:

    def on_button1_clicked(self, widget):
        cur_page_name = self.stack.get_visible_child_name()
        i = self.page_names.index(cur_page_name)
        if i == 0: return
        self.stack.set_visible_child_name(self.page_names[i-1])
    

    Or maybe you extract the page number from the child name (e.g. 0 from page0) using RegEx and generate the next page's name. There's many ways to accomplish this, I personally would keep a variable of all the pages and use that to determine which page is next/prev.