### GTK4 Label Widget Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/display-widgets/label.md Demonstrates various Gtk.Label configurations including alignment, justification, line wrapping, and Pango markup. This example requires a GTK application setup. ```Python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk class LabelWindow(Gtk.ApplicationWindow): def __init__(self, **kargs): super().__init__(**kargs, title="Label Demo") box = Gtk.Box(spacing=10) self.set_child(box) box_left = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=10, hexpand=True, homogeneous=True, ) box_right = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) box.append(box_left) box.append(box_right) label = Gtk.Label(label="This is a normal label") box_left.append(label) label = Gtk.Label( label="This is a normal label with xalign set to 0", xalign=0, ) box_left.append(label) label = Gtk.Label( label="This is a left-justified label.\nWith multiple lines.", justify=Gtk.Justification.LEFT, ) box_left.append(label) label = Gtk.Label( label="This is a right-justified label.\nWith multiple lines.", justify=Gtk.Justification.RIGHT, ) box_left.append(label) label = Gtk.Label( label="This is an example of a line-wrapped label. It " "should not be taking up the entire " "width allocated to it, but automatically " "wraps the words to fit.\n" " It supports multiple paragraphs correctly, " "and correctly adds " "many extra spaces. ", wrap=True, max_width_chars=32, ) box_right.append(label) label = Gtk.Label( label="This is an example of a line-wrapped, filled label. " "It should be taking " "up the entire width allocated to it. " "Here is a sentence to prove " "my point. Here is another sentence. " "Here comes the sun, do de do de do.\n" " This is a new paragraph.\n" " This is another newer, longer, better " "paragraph. It is coming to an end, " "unfortunately.", wrap=True, justify=Gtk.Justification.FILL, max_width_chars=32, ) box_right.append(label) label = Gtk.Label(wrap=True, max_width_chars=48) label.set_markup( "Text can be small, big, " "bold, italic and even point to " 'somewhere in the internets.' ) box_left.append(label) button = Gtk.Button(label="Click at your own risk") label = Gtk.Label( label="_Press Alt + P to select button to the right", use_underline=True, selectable=True, mnemonic_widget=button, ) box_left.append(label) box_right.append(button) def on_activate(app): win = LabelWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Menu Popover Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/popovers.md Illustrates creating a Gtk.MenuButton that displays a Gtk.PopoverMenu populated from a Gio.MenuModel defined in XML. This setup is common for application menus. ```XML
About app.about Quit app.quit
``` ```Python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gio, Gtk # This would typically be its own file MENU_XML = """
About app.about Quit app.quit
" class AppWindow(Gtk.ApplicationWindow): def __init__(self, **kwargs): super().__init__(**kwargs) self.set_default_size(300, 200) headerbar = Gtk.HeaderBar() self.set_titlebar(headerbar) builder = Gtk.Builder.new_from_string(MENU_XML, -1) menu_model = builder.get_object("app-menu") button = Gtk.MenuButton(menu_model=menu_model) headerbar.pack_end(button) class Application(Gtk.Application): def __init__(self, **kwargs): super().__init__(application_id="com.example.App", **kwargs) self.window = None def do_startup(self): Gtk.Application.do_startup(self) action = Gio.SimpleAction(name="about") action.connect("activate", self.on_about) self.add_action(action) action = Gio.SimpleAction(name="quit") action.connect("activate", self.on_quit) self.add_action(action) def do_activate(self): # We only allow a single window and raise any existing ones if not self.window: # Windows are associated with the application # when the last one is closed the application shuts down self.window = AppWindow(application=self, title="Main Window") self.window.present() def on_about(self, action, param): about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True) about_dialog.present() def on_quit(self, action, param): self.quit() app = Application() app.run(None) ``` -------------------------------- ### Set Up Python Virtual Environment and Install Dependencies Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/dev_environ.md Create a virtual environment and install necessary build tools and libraries for PyGObject development with Pip. ```console python3 -m venv .venv source .venv/bin/activate python3 -m pip install meson-python meson ninja pycairo pytest pre-commit ``` -------------------------------- ### GTK4 Layout Containers Example Source: https://context7.com/gnome/pygobject/llms.txt Demonstrates the use of Gtk.Box, Gtk.Grid, Gtk.Stack, and Gtk.ListBox for arranging widgets in a GTK4 application. Includes setup for application, window, header bar, and interactive elements like a stack switcher and a sortable/filterable list box. ```python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk, GObject class LayoutDemo(Gtk.ApplicationWindow): def __init__(self, **kwargs): super().__init__(**kwargs, title="Layout Demo", default_width=600, default_height=400) # Outer vertical box vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin_top=12, margin_bottom=12, margin_start=12, margin_end=12) self.set_child(vbox) # --- HeaderBar as titlebar --- header = Gtk.HeaderBar() header.pack_start(Gtk.Button(label="Back")) header.pack_end(Gtk.Button(icon_name="open-menu-symbolic")) self.set_titlebar(header) # --- Grid --- grid = Gtk.Grid(column_spacing=6, row_spacing=6) for col in range(3): for row in range(2): btn = Gtk.Button(label=f"[{col},{row}]") grid.attach(btn, col, row, 1, 1) vbox.append(grid) # --- Stack + StackSwitcher --- stack = Gtk.Stack(transition_type=Gtk.StackTransitionType.SLIDE_LEFT_RIGHT) stack_switcher = Gtk.StackSwitcher(stack=stack) header.set_title_widget(stack_switcher) page_a = Gtk.Label(label="Page A content", vexpand=True) page_b = Gtk.Label(label="Page B content", vexpand=True) stack.add_titled(page_a, "a", "Page A") stack.add_titled(page_b, "b", "Page B") vbox.append(stack) # --- ListBox with sort/filter --- listbox = Gtk.ListBox(selection_mode=Gtk.SelectionMode.SINGLE, show_separators=True) for word in ["Zebra", "Apple", "Mango", "Beta", "Cherry"]: row = Gtk.ListBoxRow() row.set_child(Gtk.Label(label=word, xalign=0)) row.data = word listbox.append(row) listbox.set_sort_func(lambda r1, r2: r1.data.lower() > r2.data.lower()) listbox.set_filter_func(lambda row: not row.data.startswith("B")) listbox.connect("row-activated", lambda lb, row: print("Selected:", row.data)) vbox.append(listbox) def on_activate(app): LayoutDemo(application=app).present() app = Gtk.Application(application_id="com.example.Layout") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Gtk.Application Example Source: https://context7.com/gnome/pygobject/llms.txt Demonstrates the usage of Gtk.Application for managing application lifecycle, setting up menus, and handling command-line arguments. ```APIDOC ## Gtk.Application — Application lifecycle and window management `Gtk.Application` is the entry point for every GTK application. It handles instance uniqueness, D-Bus activation, startup/shutdown lifecycle, menus, and command-line parsing. Override `do_startup()` to set up actions and menus, and `do_activate()` to create the first window. Use `Gio.ApplicationFlags` to control instance behavior. ```python import sys import gi gi.require_version("Gtk", "4.0") from gi.repository import GLib, Gio, Gtk MENU_XML = """ Window
app.about _About app.quit _Quit <Primary>q
""" class AppWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_default_size(400, 300) self.props.show_menubar = True # Window-scoped action with state ("win.maximize") max_action = Gio.SimpleAction.new_stateful( "maximize", None, GLib.Variant.new_boolean(False) ) max_action.connect("change-state", self.on_maximize_toggle) self.add_action(max_action) self.label = Gtk.Label(label="Hello World") self.set_child(self.label) def on_maximize_toggle(self, action, value): action.set_state(value) self.maximize() if value.get_boolean() else self.unmaximize() class Application(Gtk.Application): def __init__(self, *args, **kwargs): super().__init__( *args, application_id="org.example.App", flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs, ) self.window = None self.add_main_option( "verbose", ord("v"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Enable verbose output", None, ) def do_startup(self): Gtk.Application.do_startup(self) action = Gio.SimpleAction.new("about", None) action.connect("activate", lambda a, p: None) self.add_action(action) action = Gio.SimpleAction.new("quit", None) action.connect("activate", lambda a, p: self.quit()) self.add_action(action) builder = Gtk.Builder.new_from_string(MENU_XML, -1) self.set_menubar(builder.get_object("menubar")) def do_activate(self): if not self.window: self.window = AppWindow(application=self, title="My App") self.window.present() def do_command_line(self, command_line): options = command_line.get_options_dict().end().unpack() if "verbose" in options: print("Verbose mode enabled") self.activate() return 0 if __name__ == "__main__": app = Application() app.run(sys.argv) ``` ``` -------------------------------- ### Setup Application Startup Actions Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/application.md Configures actions for 'about' and 'quit' during application startup. It also loads the menu bar from an XML string. ```python action = Gio.SimpleAction.new("about", None) action.connect("activate", self.on_about) self.add_action(action) ``` ```python action = Gio.SimpleAction.new("quit", None) action.connect("activate", self.on_quit) self.add_action(action) ``` ```python builder = Gtk.Builder.new_from_string(MENU_XML, -1) self.set_menubar(builder.get_object("menubar")) ``` -------------------------------- ### Gtk.FlowBox Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/layout-widgets.md Demonstrates creating a Gtk.FlowBox and populating it with color swatch buttons. This example shows how to set up the FlowBox, add children, and define custom drawing for child widgets. ```Python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk, Gdk class FlowBoxWindow(Gtk.ApplicationWindow): def __init__(self, **kargs): super().__init__(**kargs, title="FlowBox Demo") self.set_default_size(300, 250) header = Gtk.HeaderBar() self.set_titlebar(header) scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.set_child(scrolled) flowbox = Gtk.FlowBox( valign=Gtk.Align.START, max_children_per_line=30, selection_mode=Gtk.SelectionMode.NONE, ) scrolled.set_child(flowbox) self.create_flowbox(flowbox) def draw_button_color(self, area, cr, width, height, rgba): cr.set_source_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha) 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(tooltip_text=str_color) area = Gtk.DrawingArea() area.set_size_request(24, 24) area.set_draw_func(self.draw_button_color, rgba) button.set_child(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.append(button) def on_activate(app): # Create window win = FlowBoxWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Install PyGObject on Ubuntu/Debian with pip Source: https://context7.com/gnome/pygobject/llms.txt Install system packages for development and then use pip to install PyGObject within a virtual environment. ```bash sudo apt install libgirepository-2.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-4.0 pip3 install pycairo PyGObject ``` -------------------------------- ### GTK4 Entry Controls Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/controls/entries.md Demonstrates the usage of Gtk.Entry, Gtk.SearchEntry, and Gtk.PasswordEntry with various configurations and interactive controls. ```Python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk, GLib class EntryWindow(Gtk.ApplicationWindow): def __init__(self, **kargs): super().__init__(**kargs, title="Entry Demo") self.set_size_request(200, 100) self.timeout_id = None header = Gtk.HeaderBar() self.set_titlebar(header) vbox = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=6, margin_start=24, margin_end=24, margin_top=24, margin_bottom=24, ) self.set_child(vbox) # Gtk.SearchEntry search = Gtk.SearchEntry( key_capture_widget=self, placeholder_text="Search Entry" ) header.set_title_widget(search) self.set_focus(search) # Gtk.Entry self.entry = Gtk.Entry(text="Hello World", progress_pulse_step=0.2) vbox.append(self.entry) hbox = Gtk.Box(spacing=6) vbox.append(hbox) self.check_editable = Gtk.CheckButton(label="Editable", active=True) self.check_editable.connect("toggled", self.on_editable_toggled) hbox.append(self.check_editable) self.check_visible = Gtk.CheckButton(label="Visible", active=True) self.check_visible.connect("toggled", self.on_visible_toggled) hbox.append(self.check_visible) self.pulse = Gtk.CheckButton(label="Pulse") self.pulse.connect("toggled", self.on_pulse_toggled) hbox.append(self.pulse) self.icon = Gtk.CheckButton(label="Icon") self.icon.connect("toggled", self.on_icon_toggled) hbox.append(self.icon) # Gtk.PasswordEntry pass_entry = Gtk.PasswordEntry( placeholder_text="Password Entry", show_peek_icon=True, margin_top=24, ) vbox.append(pass_entry) def on_editable_toggled(self, button): value = button.get_active() self.entry.set_editable(value) def on_visible_toggled(self, button): self.entry.props.visibility = button.props.active def on_pulse_toggled(self, button): if button.get_active(): # Call self.do_pulse every 100 ms self.timeout_id = GLib.timeout_add(100, self.do_pulse) else: # Don't call self.do_pulse anymore GLib.source_remove(self.timeout_id) self.timeout_id = None def do_pulse(self): self.entry.progress_pulse() return True def on_icon_toggled(self, button): icon_name = "system-search-symbolic" if button.props.active else None self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, icon_name) def on_activate(app): win = EntryWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Install PyGObject on openSUSE (Pip) Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Install PyGObject and its dependencies from PyPI using pip on openSUSE. Ensure build dependencies are installed via Zypper before using pip. ```bash sudo zypper install cairo-devel pkg-config python3-devel gcc gobject-introspection-devel ``` ```bash pip3 install pycairo ``` ```bash pip3 install PyGObject ``` ```bash python3 hello.py ``` -------------------------------- ### Install PyGObject with Pixi Source: https://context7.com/gnome/pygobject/llms.txt Initialize a new project with Pixi and add PyGObject and GTK4. ```bash pixi init . pixi add pygobject gtk4 pixi run python hello.py ``` -------------------------------- ### Build PyGObject with Meson Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/packagingguide.md Use these commands to set up, compile, and install PyGObject using Meson. Ensure the DESTDIR is set correctly for staging. ```bash meson setup --prefix /usr --buildtype=plain _build -Dc_args=... -Dc_link_args=... meson compile -C _build meson test -C _build DESTDIR=/path/to/staging/root meson install -C _build ``` -------------------------------- ### Install PyGObject on openSUSE (System Package) Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Install PyGObject and GTK4 using the Zypper package manager on openSUSE. This method uses system packages for installation. ```bash sudo zypper install python3-gobject python3-gobject-Gdk typelib-1_0-Gtk-4_0 libgtk-4-1 ``` ```bash python3 hello.py ``` -------------------------------- ### Simple GTK Application with PyGObject Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md This is a basic GTK application that displays a 'Hello World' window. It requires PyGObject and GTK to be installed. ```python import gi gi.require_version("Gtk", "4.0") from gi.repository import GLib, Gtk class MyApplication(Gtk.Application): def __init__(self): super().__init__(application_id="com.example.MyGtkApplication") GLib.set_application_name('My Gtk Application') def do_activate(self): window = Gtk.ApplicationWindow(application=self, title="Hello World") window.present() app = MyApplication() app.run() ``` -------------------------------- ### GTK4 Drag and Drop Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/drag-and-drop.md This is the main application code for the drag and drop example. It sets up the main window, including a source view (FlowBox) and a target view (Stack). ```python import gi gi.require_version("Gdk", "4.0") gi.require_version("Gtk", "4.0") from gi.repository import Gdk, GObject, Gtk class DragDropWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kargs): super().__init__(*args, **kargs, title="Drag and Drop Example") self.set_default_size(500, 400) views_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, vexpand=True) self.set_child(views_box) flow_box = Gtk.FlowBox(selection_mode=Gtk.SelectionMode.NONE) views_box.append(flow_box) flow_box.append(SourceFlowBoxChild("Item 1", "image-missing")) flow_box.append(SourceFlowBoxChild("Item 2", "help-about")) flow_box.append(SourceFlowBoxChild("Item 3", "edit-copy")) views_box.append(Gtk.Separator()) self.target_view = TargetView(vexpand=True) views_box.append(self.target_view) class SourceFlowBoxChild(Gtk.FlowBoxChild): def __init__(self, name, icon_name): super().__init__() self.name = name self.icon_name = icon_name box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) self.set_child(box) icon = Gtk.Image(icon_name=self.icon_name) label = Gtk.Label(label=self.name) box.append(icon) box.append(label) drag_controller = Gtk.DragSource() drag_controller.connect("prepare", self.on_drag_prepare) drag_controller.connect("drag-begin", self.on_drag_begin) self.add_controller(drag_controller) def on_drag_prepare(self, _ctrl, _x, _y): item = Gdk.ContentProvider.new_for_value(self) string = Gdk.ContentProvider.new_for_value(self.name) return Gdk.ContentProvider.new_union([item, string]) def on_drag_begin(self, ctrl, _drag): icon = Gtk.WidgetPaintable.new(self) ctrl.set_icon(icon, 0, 0) class TargetView(Gtk.Box): def __init__(self, **kargs): super().__init__(**kargs) self.stack = Gtk.Stack(hexpand=True) self.append(self.stack) empty_label = Gtk.Label(label="Drag some item, text, or files here.") self.stack.add_named(empty_label, "empty") self.stack.set_visible_child_name("empty") box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, vexpand=True, valign=Gtk.Align.CENTER, ) self.stack.add_named(box, "item") self.icon = Gtk.Image() box.append(self.icon) self.label = Gtk.Label() box.append(self.label) self.text = Gtk.Label() self.stack.add_named(self.text, "other") drop_controller = Gtk.DropTarget.new( type=GObject.TYPE_NONE, actions=Gdk.DragAction.COPY ) drop_controller.set_gtypes([SourceFlowBoxChild, Gdk.FileList, str]) drop_controller.connect("drop", self.on_drop) self.add_controller(drop_controller) def on_drop(self, _ctrl, value, _x, _y): if isinstance(value, SourceFlowBoxChild): self.label.props.label = value.name self.icon.props.icon_name = value.icon_name self.stack.set_visible_child_name("item") elif isinstance(value, Gdk.FileList): files = value.get_files() names = "" for file in files: names += f"Loaded file {file.get_basename()}\n" self.text.props.label = names self.stack.set_visible_child_name("other") elif isinstance(value, str): self.text.props.label = value self.stack.set_visible_child_name("other") def on_activate(app): win = DragDropWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Import and Print Gtk Module Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/introspection_porting.md Demonstrates how to import the Gtk module from gi.repository and print its representation. This confirms that the Gtk typelib is installed and accessible. ```console from gi.repository import Gtk; print Gtk ``` -------------------------------- ### Install Pre-commit Hook Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/style_guide.md Install the pre-commit hook to automatically format code before committing. Run this command in your project's root directory. ```bash pre-commit install ``` -------------------------------- ### Install PyGObject from PyPI Source: https://github.com/gnome/pygobject/blob/main/README.rst Install the latest version of PyGObject using pip. A C compiler is required as PyGObject is distributed as a source distribution. ```bash pip install PyGObject ``` -------------------------------- ### PyGTK 2 Hello World Message Dialog Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/introspection_porting.md Example of creating a simple message dialog using PyGTK 2. ```console $ python -c 'import gtk; gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, "Hello World").run()' ``` -------------------------------- ### Initialize GObject Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gobject/basics.md GObjects are initialized like any other Python class. This example shows the basic initialization of a GTK label. ```python label = Gtk.Label() ``` -------------------------------- ### Install PyGObject on Ubuntu/Debian (System Package) Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Install PyGObject and GTK4 using the system's package manager on Ubuntu or Debian. This is a straightforward method for getting started. ```bash sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-4.0 ``` ```bash python3 hello.py ``` -------------------------------- ### Gtk.Application Lifecycle and Window Management Source: https://context7.com/gnome/pygobject/llms.txt This snippet demonstrates the structure of a GTK application, including setting up menus, actions, and handling window creation and command-line arguments. Override do_startup() for setup and do_activate() for window creation. ```python import sys import gi gi.require_version("Gtk", "4.0") from gi.repository import GLib, Gio, Gtk MENU_XML = """ Window
app.about _About app.quit _Quit <Primary>q
" class AppWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_default_size(400, 300) self.props.show_menubar = True # Window-scoped action with state ("win.maximize") max_action = Gio.SimpleAction.new_stateful( "maximize", None, GLib.Variant.new_boolean(False) ) max_action.connect("change-state", self.on_maximize_toggle) self.add_action(max_action) self.label = Gtk.Label(label="Hello World") self.set_child(self.label) def on_maximize_toggle(self, action, value): action.set_state(value) self.maximize() if value.get_boolean() else self.unmaximize() class Application(Gtk.Application): def __init__(self, *args, **kwargs): super().__init__( *args, application_id="org.example.App", flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs, ) self.window = None self.add_main_option( "verbose", ord("v"), GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Enable verbose output", None, ) def do_startup(self): Gtk.Application.do_startup(self) action = Gio.SimpleAction.new("about", None) action.connect("activate", lambda a, p: None) self.add_action(action) action = Gio.SimpleAction.new("quit", None) action.connect("activate", lambda a, p: self.quit()) self.add_action(action) builder = Gtk.Builder.new_from_string(MENU_XML, -1) self.set_menubar(builder.get_object("menubar")) def do_activate(self): if not self.window: self.window = AppWindow(application=self, title="My App") self.window.present() def do_command_line(self, command_line): options = command_line.get_options_dict().end().unpack() if "verbose" in options: print("Verbose mode enabled") self.activate() return 0 if __name__ == "__main__": app = Application() app.run(sys.argv) ``` -------------------------------- ### Gtk4 Clipboard Example Application Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/clipboard.md This example demonstrates how to copy and paste text and images using Gdk.Clipboard in a Gtk4 application. It includes setup for the application window, clipboard access, and handlers for copy/paste actions. ```Python import gi gi.require_version("Gdk", "4.0") gi.require_version("Gtk", "4.0") from gi.repository import Gdk, Gtk class ClipboardWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kargs): super().__init__(*args, **kargs, title="Clipboard Example") box = Gtk.Box(spacing=12, orientation=Gtk.Orientation.VERTICAL) self.set_child(box) self.clipboard = Gdk.Display.get_default().get_clipboard() text_box = Gtk.Box(spacing=6, homogeneous=True) box.append(text_box) self.entry = Gtk.Entry(text="Some text you can copy") button_copy_text = Gtk.Button(label="Copy Text") button_copy_text.connect("clicked", self.copy_text) button_paste_text = Gtk.Button(label="Paste Text") button_paste_text.connect("clicked", self.paste_text) text_box.append(self.entry) text_box.append(button_copy_text) text_box.append(button_paste_text) image_box = Gtk.Box(spacing=6) box.append(image_box) self.picture = Gtk.Picture.new_for_filename("../images/application.png") self.picture.props.hexpand = True button_copy_image = Gtk.Button(label="Copy Image", valign=Gtk.Align.CENTER) button_copy_image.connect("clicked", self.copy_image) button_paste_image = Gtk.Button(label="Paste Image", valign=Gtk.Align.CENTER) button_paste_image.connect("clicked", self.paste_image) image_box.append(self.picture) image_box.append(button_copy_image) image_box.append(button_paste_image) def copy_text(self, _button): self.clipboard.set(self.entry.get_text()) def paste_text(self, _button): self.clipboard.read_text_async(None, self.on_paste_text) def on_paste_text(self, _clipboard, result): text = self.clipboard.read_text_finish(result) if text is not None: self.entry.set_text(text) def copy_image(self, _button): texture = self.picture.get_paintable() gbytes = texture.save_to_png_bytes() content = Gdk.ContentProvider.new_for_bytes("image/png", gbytes) self.clipboard.set_content(content) def paste_image(self, _button): self.clipboard.read_texture_async(None, self.on_paste_image) def on_paste_image(self, _clipboard, result): texture = self.clipboard.read_texture_finish(result) if texture is not None: self.picture.set_paintable(texture) def on_activate(app): win = ClipboardWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Set Up Meson Build Directory Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/dev_environ.md Initialize the Meson build system for the project. This command is typically needed only once. ```console meson setup _build ``` -------------------------------- ### GtkTextView Setup and Event Handling Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/textview.md Sets up a GtkTextView with various wrapping options and connects signals for handling user interactions like toggling wrap modes and applying tags. ```Python radio_wrapchar, radio_wrapnone, Gtk.PositionType.RIGHT, 1, 1 ) radio_wrapword = Gtk.CheckButton(label="Word Wrapping", group=radio_wrapnone) grid.attach_next_to( radio_wrapword, radio_wrapchar, Gtk.PositionType.RIGHT, 1, 1 ) radio_wrapnone.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.NONE) radio_wrapchar.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.CHAR) radio_wrapword.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.WORD) def on_button_clicked(self, _widget, tag): bounds = self.textbuffer.get_selection_bounds() if len(bounds) != 0: start, end = bounds self.textbuffer.apply_tag(tag, start, end) def on_clear_clicked(self, _widget): start = self.textbuffer.get_start_iter() end = self.textbuffer.get_end_iter() self.textbuffer.remove_all_tags(start, end) def on_wrap_toggled(self, _widget, mode): self.textview.props.wrap_mode = mode def on_justify_toggled(self, _widget, justification): self.textview.props.justification = justification def on_search_clicked(self, _widget): self.search_dialog = SearchDialog(self) self.search_dialog.button.connect("clicked", self.on_find_clicked) self.search_dialog.present() def on_find_clicked(self, _button): cursor_mark = self.textbuffer.get_insert() start = self.textbuffer.get_iter_at_mark(cursor_mark) if start.get_offset() == self.textbuffer.get_char_count(): start = self.textbuffer.get_start_iter() self.search_and_mark(self.search_dialog.entry.get_text(), start) def search_and_mark(self, text, start): end = self.textbuffer.get_end_iter() match = start.forward_search(text, 0, end) if match is not None: match_start, match_end = match self.textbuffer.apply_tag(self.tag_found, match_start, match_end) self.search_and_mark(text, match_end) def on_activate(app): win = TextViewWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Install pyenv on macOS Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/dev_environ.md Installs pyenv using Homebrew, then installs and sets Python 3.11 as the global version. ```console brew install pyenv pyenv install 3.11 pyenv global 3.11 ``` -------------------------------- ### Async Download Window Example Source: https://github.com/gnome/pygobject/blob/main/docs/guide/asynchronous.md Illustrates asynchronous file downloading with UI feedback using asyncio and Gtk. Use this for I/O-bound tasks that should not block the main thread. ```python import asyncio import time import gi gi.require_version("Gtk", "4.0") from gi.repository import Gio, GLib, Gtk from gi.events import GLibEventLoopPolicy class DownloadWindow(Gtk.ApplicationWindow): def __init__(self, *args, **kwargs): super().__init__( *args, **kwargs, default_width=500, default_height=400, title="Async I/O Example", ) self.background_task = None self.cancel_button = Gtk.Button(label="Cancel") self.cancel_button.connect("clicked", self.on_cancel_clicked) self.cancel_button.set_sensitive(False) self.start_button = Gtk.Button(label="Load") self.start_button.connect("clicked", self.on_start_clicked) textview = Gtk.TextView(vexpand=True) self.textbuffer = textview.get_buffer() scrolled = Gtk.ScrolledWindow() scrolled.set_child(textview) box = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=6, margin_start=12, margin_end=12, margin_top=12, margin_bottom=12, ) box.append(self.start_button) box.append(self.cancel_button) box.append(scrolled) self.set_child(box) def append_text(self, text): iter_ = self.textbuffer.get_end_iter() self.textbuffer.insert(iter_, f"[{time.time()}] {text}\n") def on_start_clicked(self, button): button.set_sensitive(False) self.cancel_button.set_sensitive(True) self.append_text("Start clicked...") self.background_task = asyncio.create_task(self.download()) def on_cancel_clicked(self, button): self.append_text("Cancel clicked...") self.background_task.cancel() async def download(self): file_ = Gio.File.new_for_uri("https://pygobject.gnome.org/") try: _success, content, _etag = await file_.load_contents_async() except GLib.GError as e: self.append_text(f"Error: {e.message}") else: content_text = content[:100].decode("utf-8") self.append_text(f"Got content: {content_text}...") finally: self.cancel_button.set_sensitive(False) self.start_button.set_sensitive(True) class Application(Gtk.Application): def do_activate(self): window = DownloadWindow(application=self) window.present() def main(): asyncio.set_event_loop_policy(GLibEventLoopPolicy()) app = Application() app.run() if __name__ == "__main__": main() ``` -------------------------------- ### Install PyGObject with Homebrew on macOS Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Use this command to install PyGObject and GTK4 using Homebrew. Ensure Homebrew is installed first. ```bash brew install pygobject3 gtk4 ``` -------------------------------- ### GTK4 Application Setup and Event Handling Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/application.md This snippet shows how to define an application class, handle 'about' and 'quit' actions, and run the application. It requires the Gtk and sys modules. ```Python import sys import gi ti.require_version('Gtk', '4.0') from gi.repository import Gtk class Application(Gtk.Application): def __init__(self): super().__init__(application_id='org.example.app') def do_activate(self): win = self.props.active_window if not win: win = Gtk.ApplicationWindow(application=self) win.present() def on_about(self, _action, _param): about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True) about_dialog.present() def on_quit(self, _action, _param): self.quit() if __name__ == "__main__": app = Application() app.run(sys.argv) ``` -------------------------------- ### Install Ubuntu/Debian Dependencies for PyGObject Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/dev_environ.md Installs necessary packages for compiling Python and pip installing PyGObject on Ubuntu or Debian-based systems. ```console sudo apt-get install -y python3-venv python3-wheel python3-dev sudo apt-get install -y gobject-introspection libgirepository-2.0-dev \ gir1.2-girepository-3.0 build-essential libbz2-dev libreadline-dev \ libssl-dev zlib1g-dev libsqlite3-dev wget curl llvm libncurses-dev \ xz-utils tk-dev libcairo2-dev ``` -------------------------------- ### Gtk.Switch Example Source: https://github.com/gnome/pygobject/blob/main/docs/tutorials/gtk4/controls/switch.md Demonstrates creating and managing Gtk.Switch widgets. Connects to the `notify::active` signal to detect state changes and prints the new state. ```Python import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk class SwitcherWindow(Gtk.ApplicationWindow): def __init__(self, **kargs): super().__init__(**kargs, title="Switch Demo") hbox = Gtk.Box(spacing=6, homogeneous=True, margin_top=24, margin_bottom=24) self.set_child(hbox) switch = Gtk.Switch(active=False, halign=Gtk.Align.CENTER) switch.connect("notify::active", self.on_switch_activated) hbox.append(switch) switch = Gtk.Switch(active=True, halign=Gtk.Align.CENTER) switch.connect("notify::active", self.on_switch_activated) hbox.append(switch) def on_switch_activated(self, switch, _gparam): state = "on" if switch.props.active else "off" print("Switch was turned", state) def on_activate(app): win = SwitcherWindow(application=app) win.present() app = Gtk.Application(application_id="com.example.App") app.connect("activate", on_activate) app.run(None) ``` -------------------------------- ### Install pyenv on Linux Source: https://github.com/gnome/pygobject/blob/main/docs/devguide/dev_environ.md Installs pyenv using a curl script, reloads the shell, and then installs and sets Python 3.11 as the global version. ```console curl https://pyenv.run | bash exec $SHELL pyenv install 3.11 pyenv global 3.11 ``` -------------------------------- ### Install PyGObject on Fedora (Pip) Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Install PyGObject and its dependencies from PyPI using pip on Fedora. Ensure all necessary build tools and libraries are installed first. ```bash sudo dnf install gcc gobject-introspection-devel cairo-gobject-devel pkg-config python3-devel gtk4 ``` ```bash pip3 install pycairo ``` ```bash pip3 install PyGObject ``` ```bash python3 hello.py ``` -------------------------------- ### Install PyGObject on Ubuntu/Debian (Pip) Source: https://github.com/gnome/pygobject/blob/main/docs/getting_started.md Install PyGObject and its dependencies from PyPI using pip on Ubuntu or Debian. This method requires installing build dependencies first. ```bash sudo apt install libgirepository-2.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-4.0 ``` ```bash pip3 install pycairo ``` ```bash pip3 install PyGObject ``` ```bash python3 hello.py ```