File chooser dialog
Contents
FileChooserDialog
The GtkFileChooserDialog is a subclass of GtkDialog (see diaogue article) and provides opening and saving of files and folders.

Glade
The dialog can be added from the "Toplevel" section of the widget sidebar. In addition to the file browser itself the widget has an intern GtkBox for additional widgets and a GtkButtonBox as "action area" for buttons.
First the Gtk.FileChooserAction mode must be defined (see Python GI API Reference): open or save file, choose or create a folder.
Action area and Responses
The response signal is emitted on widget interaction in the action area which also passes the response value. So for these widgets there is no need to activate the clicked signal of buttons.
By default the "action area" is generated beneath the file browser area.

If the FileChooserDialog is used without Glade (see below) the buttons are created in the headerbar. This seems to be standard procedure because Glade generated dialogs induce the warning
Gtk-WARNING **: Content added to the action area of a dialog using header bars
This message is not shown if buttons are not added to the intern action area.
If a headerbar with buttons is created in Glade the buttons cannot be assigned to a response value.
There may be several solutions to the problem:
XML file
After creating a headerbar with button(s) the Glade file is opened in a text editor and add line(s) to the <action-widgets>
element:
<object class="GtkFileChooserDialog" id="filechooser_dialog"> <property ... ></property> <property ... ></property> <!-- ... --> <action-widgets> <!-- Buttons innerhalb der action area --> <action-widget response="0">button1</action-widget> <action-widget response="1">button2</action-widget> <!-- Button in Headerbar --> <action-widget response="-1">hb_button</action-widget> </action-widgets> <!-- ... --> </object>
This works but this procedure is surely not the intended way to deal with the problem because after altering the Glade file the edit is retracted.
add_action_widget function
The add_action_widget
adds activatable widgets to the action area and hold a response value. This includes widgets of the Gtk.Activatable class: Buttons, MenuItem, RecentChooserMenu, Switch and ToolItem.
The scheme for creating a button is
widget.add_action_widget(button,response)
The widget property "can-default" of the button must be activated:
button.set_property("can-default",True)
In the example the standard buttons "apply/cancel" are added to the file dialog:
button = Gtk.Button.new_from_stock(Gtk.STOCK_CANCEL) button.set_property("can-default",True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL) button = Gtk.Button.new_from_stock(Gtk.STOCK_APPLY) button.set_property("can-default",True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK)
To apply file selection on doubleclick the file-activated is also required in addition to the response signal.
Preview widget
The dialogue can contain an optional preview widget. To use it activate "Preview Widget Active" and choose a free widget (p.e. a GtkImage). It may be necessary to create the preview widget in an empty container widget and pull it into a free area.
If the preview requires a refresh the update-preview signal is emitted.
FileFilter
Files can be filtered according to certain criteria by using FileFilter. There can be defined several (shell style glob) patterns or MIME-types for each filter.
In Glade filters can be found in the widget sidebar in the "Miscellaneous" group. A filter for a dialog can be selected in the general widget properties. This corresponds to the set_filter
function.
Python
Dialog without Glade
The FileChooserDialog is a complex but also easy to use graphic interface item. Realizing the dialog without Glade also avoids the headerbar problem discussed above. Creating a dialog follows the scheme
dialog = Gtk.FileChooserDialog("window title", parent_window, file_chooser_action, (button1,response1, button2,response2))
The dialog then can be directly run and processed:
response = dialog.run() if response == response1: ... elif response == response2: ... dialog.destroy()
FileFilter
There are two possibilities to apply a FileFilter:
No user choice. The applied filter is preset:
dialog.set_filter(filter)
Selection per dropdown menu. The user can choose between different defined filters:
dialog.add_filter(filter1) dialog.add_filter(filter2) ...
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkFileFilter" id="filefilter"> <mime-types> <mime-type>image/*</mime-type> </mime-types> </object> <object class="GtkFileFilter" id="jpg_filter"> <mime-types> <mime-type>image/jpeg</mime-type> </mime-types> </object> <object class="GtkFileFilter" id="png_filter"> <mime-types> <mime-type>image/png</mime-type> </mime-types> </object> <object class="GtkImage" id="preview"> <property name="width_request">200</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="margin_right">5</property> </object> <object class="GtkApplicationWindow" id="window"> <property name="width_request">300</property> <property name="height_request">200</property> <property name="can_focus">False</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="homogeneous">True</property> <child> <object class="GtkButton" id="file_button"> <property name="label" translatable="yes">Choose an image file...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_file_button_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="dir_button"> <property name="label" translatable="yes">Choose folder...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_dir_button_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> <child type="titlebar"> <placeholder/> </child> </object> <object class="GtkFileChooserDialog" id="filechooser_dialog"> <property name="width_request">800</property> <property name="height_request">500</property> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="attached_to">window</property> <property name="preview_widget">preview</property> <property name="use_preview_label">False</property> <signal name="delete-event" handler="on_dialog_close" swapped="no"/> <signal name="file-activated" handler="on_filechooser_dialog_file_activated" swapped="no"/> <signal name="response" handler="on_filechooser_dialog_response" swapped="no"/> <signal name="update-preview" handler="on_filechooser_dialog_update_preview" swapped="no"/> <child internal-child="vbox"> <object class="GtkBox" id="fcbox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <child> <object class="GtkButton" id="button2"> <property name="label">gtk-cancel</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkButton" id="button1"> <property name="label">gtk-apply</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">3</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> <action-widgets> <action-widget response="-6">button2</action-widget> <action-widget response="-5">button1</action-widget> </action-widgets> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Choose image...</property> <property name="show_close_button">True</property> </object> </child> <action-widgets> <action-widget response="-6">button2</action-widget> <action-widget response="-5">button1</action-widget> </action-widgets> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import os import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio, GdkPixbuf class Handler: def on_window_destroy(self, window): window.close() def on_dialog_close(self, widget, *event): widget.hide_on_delete() return True def on_filechooser_dialog_response(self, widget, response): if response == -6: print("Cancel") elif response == -5: print("File selection: {}".format(widget.get_filename())) self.on_dialog_close(widget) def on_filechooser_dialog_file_activated(self, widget): self.on_filechooser_dialog_response(widget, -5) def on_filechooser_dialog_update_preview(self, widget): if widget.get_filename() != None and os.path.isfile(widget.get_filename()): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(widget.get_filename(), 200, 200, True, ) app.obj("preview").set_from_pixbuf(pixbuf) def on_file_button_clicked(self,widget): app.obj("filechooser_dialog").show_all() def on_dir_button_clicked(self,widget): dialog = Gtk.FileChooserDialog(title="Choose a folder", parent=app.obj("window"), action=Gtk.FileChooserAction.SELECT_FOLDER, ) dialog.set_default_size(600, 300) dialog.add_buttons("Cancel", Gtk.ResponseType.CANCEL, "OK", Gtk.ResponseType.OK) response = dialog.run() if response == Gtk.ResponseType.OK: print("Folder selection: {}".format(dialog.get_filename())) elif response == Gtk.ResponseType.CANCEL: print("Cancel") dialog.destroy() class ExampleApp: def __init__(self): self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0)) self.app.connect("activate", self.on_app_activate) self.app.connect("shutdown", self.on_app_shutdown) def on_app_activate(self, app): builder = Gtk.Builder() builder.add_from_file("16_filechooser.glade") builder.connect_signals(Handler()) self.obj = builder.get_object self.obj("window").set_application(app) self.obj("window").show_all() #add filters to filechooser dialog self.obj("filefilter").set_name("Image files") self.obj("filechooser_dialog").add_filter(self.obj("filefilter")) self.obj("png_filter").set_name("PNG files") self.obj("filechooser_dialog").add_filter(self.obj("png_filter")) self.obj("jpg_filter").set_name("JPG files") self.obj("filechooser_dialog").add_filter(self.obj("jpg_filter")) #add buttons to headerbar of Glade generated dialog button = Gtk.Button.new_with_label("Cancel") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.CANCEL) button = Gtk.Button.new_with_label("OK") button.set_property("can-default", True) self.obj("filechooser_dialog").add_action_widget(button, Gtk.ResponseType.OK) def on_app_shutdown(self, app): self.app.quit() def run(self, argv): self.app.run(argv) app = ExampleApp() app.run(sys.argv)
Desktop integration
Contents
Desktop integration: icon, headerbar, commndline options
(Continuation if the GtkApplication article)

Glade
Icon
To assign an icon to an window just select "General Appearance > Icon File". Problematic here is that Glade only shows image files located in the same folder as the Glade file even if an image from another folder is chosen.
A simple solution is editing the Glade file in a text editor and add the relative path to the icon. This edit is preserved even when changing and saving the file with Glade again:
<object class="GtkApplicationWindow" id="window"> ... <!-- <property name="icon">duckyou.svg</property> --> <property name="icon">../files/duckyou.svg</property> ...
Headerbar
Headerbars were introduced in GNOME 3.10 and unite titlebar and toolbar. Besides title and subtitle there is room for widgets such as buttons or menus and client side window controls.
A headerbar is optional. To make use of it "General > Appearance > Client side window decorations" has to be activated if not set yet. This prepares a reserved container area in the upper window area to add the headerbar widget in. If a headerbar is placed out of this specific area a regular titlebar is generated in addition to the headerbar.

Commandline options
GtkApplication provides functions to define individual commandline options of the applications (Handling command line options in GApplication).
Create Options
Options are added by the add_main_option_entries(entrylist)
function. The entries must be GLib.OptionEntry formatted which requires a bunch of parameters.
def __init__(self): self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0)) self.app.add_main_option_entries([ self.create_option_entry("--version", description="Show version numbers and exit"), self.create_option_entry("--setlabel", description="Set label widget", arg=GLib.OptionArg.STRING,), self.create_option_entry("--bollocks", description="Additional test option - exit"), ]) def create_option_entry(self, long_name, short_name=None, flags=0, arg=GLib.OptionArg.NONE, arg_data=None, description=None, arg_description=None): option = GLib.OptionEntry() option.long_name = long_name.lstrip('-') option.short_name = 0 if not short_name else ord(short_name.lstrip('-')) option.flags = flags option.arg = arg option.arg_data = arg_data option.description = description option.arg_description = arg_description return option
Short names
An option can have a one character synonym ("a printable ASCII character different from ‘-‘" to be precise), the short name. Lokking at the option --help
this commonly is -h
.
The short_name
variable of OptionEntry ist surprisingly integer. The not very obvious solution here is to pass the character's decimal code, p.e. 97 for "a". An error message will be thrown when trying to pass invalid numbers. Options without a short code get a value of 0.
Connect signal
The "handle-local-options" signal of Gtk.Application handles commandline options. If the signal is connected the signal is emitted before the "startup" signal.
self.app.connect("handle-local-options", self.on_local_option)
Processing options
The option
will be passed as an element of the GLib.VariantDict class which can be searched for by calling contains("option")
:
def on_local_option(self, app, option): if option.contains("option1"): #do something and exit normally return 0 elif option.contains("option2"): #do something different and exit return 0 elif option.contains("option3"): #do more and continue return -1
A string can be extracted by calling end()
which converts GLib.VariantDict to a GLib.Variant element. That GLib.Variant then can be culled by calling keys()
:
var = GLib.VariantDict.end(option) option_string = var[var.keys()[0]]
The handler function demands a return value that corresponds to the exit status:
-1: application execution will be continued
0: execution successful, application will be quit, "startup/activate" will not be emitted
1 or positive value: execution was not successful, application will be quit
Run application with options
The option --help
is always available and lists all defined options of the application and their descriptions.
The options of the example file now can be executed:
$ python script.py --version Python: 3.6.0 GTK+: 3.22.6
or pass a string to the application's Gtk.Label:
$ python script.py --setlabel "I can haz options!"
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkMenu" id="menu"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">MenuItem 1</property> <property name="use_underline">True</property> </object> </child> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">MenuItem 2</property> <property name="use_underline">True</property> </object> </child> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">MenuItem 3</property> <property name="use_underline">True</property> </object> </child> </object> <object class="GtkApplicationWindow" id="window"> <property name="width_request">400</property> <property name="height_request">300</property> <property name="can_focus">False</property> <property name="title" translatable="yes">Titel</property> <property name="icon">../files/duckyou.svg</property> <property name="show_menubar">False</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkImage"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="pixbuf">../files/duckyou.svg</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkLabel" id="label"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="pack_type">end</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkSeparator"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Titel</property> <property name="subtitle">Untertitel</property> <property name="show_close_button">True</property> <child> <object class="GtkMenuButton"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="popup">menu</property> <child> <placeholder/> </child> </object> </child> <child> <object class="GtkButton"> <property name="label">gtk-no</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> <property name="always_show_image">True</property> </object> <packing> <property name="pack_type">end</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkButton"> <property name="label">gtk-yes</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> <property name="always_show_image">True</property> </object> <packing> <property name="pack_type">end</property> <property name="position">2</property> </packing> </child> </object> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import sys import setproctitle import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio, GLib class ExampleApp: def __init__(self): setproctitle.setproctitle("Application test") GLib.set_prgname("testapp") self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0)) self.app.add_main_option_entries([ self.create_option_entry("--version", description="Show version numbers and exit", ), self.create_option_entry("--setlabel", description="Set label widget", arg=GLib.OptionArg.STRING, ), self.create_option_entry("--bollocks", description="Additional test option - exit", ), ]) self.app.connect("handle-local-options", self.on_local_option) self.app.connect("activate", self.on_app_activate) def on_local_option(self, app, option): self.option_string = "" if option.contains("version"): var = GLib.VariantDict.end(option) print("Python: {}".format(sys.version[:5])) print("GTK+: {}.{}.{}".format(Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION, Gtk.MICRO_VERSION, )) return 0 elif option.contains("bollocks"): return 1 elif option.contains("setlabel"): var = GLib.VariantDict.end(option) self.option_string = var[var.keys()[0]] return -1 def create_option_entry(self, long_name, short_name=None, flags=0, arg=GLib.OptionArg.NONE, arg_data=None, description=None, arg_description=None, ): option = GLib.OptionEntry() option.long_name = long_name.lstrip("-") option.short_name = 0 if not short_name else ord(short_name.lstrip("-")) option.flags = flags option.arg = arg option.arg_data = arg_data option.description = description option.arg_description = arg_description return option def on_app_activate(self, app): builder = Gtk.Builder() builder.add_from_file("15_application.glade") self.obj = builder.get_object self.obj("window").set_application(app) self.obj("label").set_text(self.option_string) self.obj("window").show_all() def run(self, argv): self.app.run(argv) app = ExampleApp() app.run(sys.argv)
Stand-alone
Contents
Run program as GtkApplication
GtkApplication handles different important aspects of a GTK+ application like GTK+ initialization, session management and desktop integration.

XML files
Glade
Glade is used as usual, main windows should be Gtk.ApplicationWindows. As example the file from the dialogue article is reused.
Python
Initialize GtkApplication
The initialization process requires the parameters application_id and flags. Flags can normally set to 0 being the same as FLAGS_NONE (see Gio.ApplicationFlags), naming conventions for application_id are listed here.
The application can be connected to different signals being emitted on preassigned events. It is mandatory to at least define an activate
signal:
def __init__(self): self.app = Gtk.Application.new("org.application.test", 0) #self.app.connect("startup", self.on_app_startup) #optional self.app.connect("activate", self.on_app_activate) #self.app.connect("shutdown", self.on_app_shutdown) #optional def on_app_activate(self, app): #setting up GtkBuilder etc. ... ... ...
Start and quit
GtkApplication takes over the handling of the GTK+ mainloop so there is no need of starting and quitting GTK+ manually and run()
and quit()
called instead:
Gtk.main() -> app.run(argv) Gtk.main_quit() -> app.quit()
If the application is quit by the [X] button or the "Quit" appmenu entry the "shutdown" signal is emitted (see above) and the program is terminated. That means there is no need to define these signals like in previous examples using GtkWindow. The "shutdown" also works even if the signal is not explicitly connected to a function during the initialization process.
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.22.1 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <property name="title" translatable="yes">Titel</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkButton" id="aboutbutton"> <property name="label" translatable="yes">GtkAboutDialog</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_aboutbutton_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="messagebutton"> <property name="label" translatable="yes">GtkMessageDialog</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_messagebutton_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> </object> <object class="GtkAboutDialog" id="aboutdialog"> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="attached_to">window</property> <property name="program_name">Glade-Tutorial</property> <property name="version">1.0</property> <property name="logo_icon_name">help-about</property> <property name="license_type">mit-x11</property> <signal name="delete-event" handler="on_dialog_delete_event" swapped="no"/> <signal name="response" handler="on_dialog_response" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> <object class="GtkBox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <property name="layout_style">end</property> <child> <object class="GtkButton" id="button1"> <property name="label">gtk-ok</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> <action-widgets> <action-widget response="-8">button1</action-widget> </action-widgets> </object> <object class="GtkMessageDialog" id="messdialog"> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="buttons">yes-no</property> <property name="text" translatable="yes"><b>MessageDialog schließen?</b></property> <property name="use_markup">True</property> <signal name="response" handler="on_dialog_response" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> <object class="GtkBox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <property name="homogeneous">True</property> <property name="layout_style">end</property> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> </object> </interface>
GMenu
<?xml version="1.0"?> <interface> <menu id="appmenu"> <section> <item> <attribute name="label" translatable="yes">About</attribute> <attribute name="action">app.about</attribute> </item> <item> <attribute name="label" translatable="yes">Message</attribute> <attribute name="action">app.message</attribute> </item> </section> <section> <item> <attribute name="label" translatable="yes">Quit</attribute> <attribute name="action">app.quit</attribute> <attribute name="accel"><Primary>q</attribute> </item> </section> </menu> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio class Handler: def on_window_destroy(self,window): window.close() def on_dialog_delete_event(self,widget,event): widget.hide_on_delete() return True def on_aboutbutton_clicked(self,widget): app.obj("aboutdialog").show_all() def on_messagebutton_clicked(self,widget): app.obj("messdialog").format_secondary_text("") app.obj("messdialog").show_all() def on_dialog_response(self,widget,response): if response == -8: widget.hide_on_delete() elif response == -9: widget.format_secondary_text("Doch!") class ExampleApp: def __init__(self): self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0), ) self.app.connect("startup", self.on_app_startup) self.app.connect("activate", self.on_app_activate) self.app.connect("shutdown", self.on_app_shutdown) def on_app_startup(self, app): print("Gio.Application startup signal emitted") def on_app_activate(self, app): print("Gio.Application activate signal emitted") builder = Gtk.Builder() builder.add_from_file("13_dialoge.glade") builder.add_from_file("14_giomenu.ui") builder.connect_signals(Handler()) app.set_app_menu(builder.get_object("appmenu")) self.obj = builder.get_object self.obj("window").set_application(app) # display application name in upper panel of the GNOME Shell (deprecated) # self.obj("window").set_wmclass("Application test","Application test") self.obj("window").show_all() self.add_simple_action("about", self.on_action_about_activated) self.add_simple_action("message", self.on_action_message_activated) self.add_simple_action("quit", self.on_action_quit_activated) def on_app_shutdown(self, app): print("Gio.Application shutdown signal emitted") def add_simple_action(self, name, callback): action = Gio.SimpleAction.new(name) action.connect("activate", callback) self.app.add_action(action) def on_action_about_activated(self, action, user_data): self.obj("aboutdialog").show_all() def on_action_message_activated(self, action, user_data): Handler().on_messagebutton_clicked(self) def on_action_quit_activated(self, action, user_data): self.app.quit() def run(self, argv): self.app.run(argv) app = ExampleApp() app.run(sys.argv)
NoN: progress
Knights of Ni - progress
As said before I am currently working on a GUI for Nikola doing some progress:
removed titlebar, using headerbar instead
switch between multiple Nikola instances
bookmark local Nikola instances (only adding for now)
- support for multilinual sites:
display of configured languages
show existent translations in posts/pages tab
"Translation" tab: show files, generate new translation file for post/page (this is somehow redundant to the posts/pages tab, I still think about it...)
if there is no title the "Title" column will show the slug or (if this does not exist neither) filename
some mor log messages to pretent important things happen

Translating articles
As soon as I figured out multilinguality in Nikola I will translate the tutorial series into English.
...
Well, that was kinda easy...(but I will need some time for translation and writing new stuff and improving the new project).
Dialogues
Contents
Handle dialogue windows
Dialogues are complementary windows which are useful in interaction with the user, to show some relevant information or demand input. The GtkDialog class provides subclasses for common dialogue use cases like the AboutDialog and MessageDialog used in the example file. (FileChooserDialog article).

Glade
In the widget sidebar dialog widgets are integrated to the "Toplevel" section next to window widgets.
Dialogs are complementary windows to grab the user's focus. They can fixed to a superordinated window but at least be configured transient to a parent window via General > Window Attributes Transient For:".
AboutDialog
The "About" dialog window in general gives information about the project, its version, license, participating programmers, translators etc. All this can be directly typed into Glade.
MessageDialog
The MessageDialog is a standard dialog to show information or call for input. It is configurated to be drawn without window decoration or showing up seperately in the taskbar. Furthermore there is the possibility to add standard buttons.
Buttons and responses
Dialogs already own an intern GtkButtonBox to place any buttons in.
In constrast to regular windows the clicked signals of the buttons do not have to be assigned in these Buttonboxes (it's still possible to do so, of course). Instead in the "General" button properties you define a response answer (int) and assign the response signal of the GtkDialog.
Standard buttons available for example in MessageDialogs have a fixed response (see also Python GI API Reference):
Ok -5
Abort -6
Close -7
Yes -8
No -9
[X] -4
The huge advantage of that procedure is that the response refers to the dialog object so the responses can be processed by a single function.
Reestablish dialogues
The problem of windows closed via destroy signal is that they cannot be reactivated therefore the delete-event signal is used here.
Python
Responses
When emitting the response signal the response is passed as parameter. As said before this offers the option to process all responses in one function:
def on_dialog_response(self,widget,response): if response == 0: widget.hide_on_delete() elif response == 1: do.something() elif response == (2 or 3): do.something.different()
Delete-event
The hide_on_delete()
function removes a window but can be reestablished by show_all()
:
def on_dialog_delete_event(self,widget,event): widget.hide_on_delete() return True
Several Glade files
As mentioned before several Glade files can be used within a project. It is not possible though to associate dialogs with their parent window if separated into different files. So the set_transient_for
function of GtkWindow is required:
dialog.set_transient_for(mainwindow)
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.22.1 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <property name="title" translatable="yes">Titel</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkButton" id="aboutbutton"> <property name="label" translatable="yes">GtkAboutDialog</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_aboutbutton_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="messagebutton"> <property name="label" translatable="yes">GtkMessageDialog</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_messagebutton_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> </object> <object class="GtkAboutDialog" id="aboutdialog"> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="attached_to">window</property> <property name="program_name">Glade-Tutorial</property> <property name="version">1.0</property> <property name="logo_icon_name">help-about</property> <property name="license_type">mit-x11</property> <signal name="delete-event" handler="on_dialog_delete_event" swapped="no"/> <signal name="response" handler="on_dialog_response" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> <object class="GtkBox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <property name="layout_style">end</property> <child> <object class="GtkButton" id="button1"> <property name="label">gtk-ok</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> <action-widgets> <action-widget response="-8">button1</action-widget> </action-widgets> </object> <object class="GtkMessageDialog" id="messdialog"> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="buttons">yes-no</property> <property name="text" translatable="yes"><b>MessageDialog schließen?</b></property> <property name="use_markup">True</property> <signal name="response" handler="on_dialog_response" swapped="no"/> <child type="titlebar"> <placeholder/> </child> <child internal-child="vbox"> <object class="GtkBox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <property name="homogeneous">True</property> <property name="layout_style">end</property> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk class Handler: def on_window_destroy(self, *args): Gtk.main_quit() def on_dialog_delete_event(self, widget, event): widget.hide_on_delete() return True def on_aboutbutton_clicked(self, widget): x.obj("aboutdialog").run() def on_messagebutton_clicked(self, widget): x.obj("messdialog").format_secondary_text("") x.obj("messdialog").run() def on_dialog_response(self, widget, response): if response == -8: widget.hide_on_delete() elif response == -9: widget.format_secondary_text("Doch!") class Example: def __init__(self): builder = Gtk.Builder() builder.add_from_file("13_dialoge.glade") builder.connect_signals(Handler()) self.obj = builder.get_object self.obj("window").show_all() def main(self): Gtk.main() x = Example() x.main()
Data view
Contents
Display TreeStore data
(Continuation of the ListStore article)

TreeStore vs. ListStore
In contrast to ListStores TreeStore rows can possess child rows. That's why the append
function requires another parameter that specifies the parent row reference:
#append row to liststore store.append([value1,value2,value3]) #append row to treestore store.append(parent,[value1,value2,value3])
The parent value is either
None if the current row is not a child row of another, or
TreeIter pointing to the superordinate row.
The TreeIter value is generated when creating a row, subordinate rows are created by
row1 = store.append(None,[value1,value2,value3]) row2 = store.append(row1,[value1,value2,value3])
The TreeIter of a cell is obtained by calling the get_selection
function of the automatically generated GtkTreeSelection widget.
Glade
In the example there are two TreeStores with some columns and the coresponding TreeView widgets to display the data columns.
TreeModelSort
Sorting a column is set by calling set_sort_column_id
. If this is applied to the TreeStore all TreeView widgets using this store are equally sorted.
If this behaviour is not diesired TreeModelSort elements come into play and which are "interposed" between store and view widgets. First the TreeModelSort is created via "Miscellaneous > Tree Model Sort" from the widget sidebar. Then you choose a source TreeView to use data from. After that the model in the TreeView widget is replaced by the newly created TreeModelSort.
The sort function is now simply applied to the TreeModelSort object instead to the TreeView object.
TreeModelFilter
TreeModelFilter allows to only show data that matches the specified filter criteria. Handling this object is analogue to TreeModelSort.
In the example the varieties can be filtered according to fruit colour so there is a GtkButtonBox required to put the corresponding buttons into.
Load formatting values from the model
Besides the columns containing displayed data there is a "weight" column in the first TreeStore. This value is used to show the cell in bold text. It is realized by setting the CellRenderer's property of "Font weight" to the column containing the corresponding value (normal font is 400). In this way the appearance of cells can be defined, for example colours or font formating.
Python
TreeModelSort
Requesting a position by calling GtkTreeSelection.get_selected()
returns a tuple (model, pos), pos of model points to TreeModelSort (or TreeModelFilter) and requires conversion to the TreeStore position:
model,pos = selection.get_selected() converted_iter = treesort.convert_iter_to_child_iter(pos) store.set_value(converted_iter,column,value)
TreeModelFilter
First of all a filter function is required defining the visibility of cells, in the example it's the variable self.color:
def color_filter_func(self,model,iter,data): if model[iter][2] == self.color: return True else: return False
This function has to be assigned to TreeFilter
treefilter.set_visible_func(filter_func)
A filter process is then executed by calling the refilter()
function on the TreeFilter object:
def on_button_clicked(self,widget): x.color = widget.get_label() x.obj("treefilter").refilter()
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkTreeStore" id="filterstore"> <columns> <!-- column-name var --> <column type="gchararray"/> <!-- column-name spec --> <column type="gchararray"/> <!-- column-name color --> <column type="gchararray"/> </columns> </object> <object class="GtkTreeModelFilter" id="treefilter"> <property name="child_model">filterstore</property> </object> <object class="GtkTreeStore" id="store"> <columns> <!-- column-name name --> <column type="gchararray"/> <!-- column-name spec --> <column type="gchararray"/> <!-- column-name color --> <column type="gchararray"/> <!-- column-name note --> <column type="gchararray"/> <!-- column-name weight --> <column type="gint"/> <!-- column-name counter --> <column type="gint"/> </columns> </object> <object class="GtkTreeModelSort" id="treesort"> <property name="model">store</property> </object> <object class="GtkApplicationWindow" id="window"> <property name="width_request">600</property> <property name="height_request">500</property> <property name="can_focus">False</property> <property name="title" translatable="yes">Titel</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child> <object class="GtkBox" id="box"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">12</property> <property name="homogeneous">True</property> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="shadow_type">in</property> <child> <object class="GtkTreeView" id="view"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="model">treesort</property> <property name="headers_clickable">False</property> <property name="rules_hint">True</property> <child internal-child="selection"> <object class="GtkTreeSelection" id="selection"/> </child> <child> <object class="GtkTreeViewColumn"> <property name="title" translatable="yes">Name</property> <property name="sort_order">descending</property> <property name="sort_column_id">5</property> <child> <object class="GtkCellRendererText"/> <attributes> <attribute name="text">0</attribute> <attribute name="weight">4</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn"> <property name="title" translatable="yes">Farbe</property> <child> <object class="GtkCellRendererText"/> <attributes> <attribute name="text">2</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn"> <property name="title" translatable="yes">Bemerkung</property> <child> <object class="GtkCellRendererText" id="cellrenderer_note"> <property name="editable">True</property> <signal name="edited" handler="on_cellrenderer_note_edited" swapped="no"/> </object> <attributes> <attribute name="text">3</attribute> </attributes> </child> </object> </child> </object> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="shadow_type">in</property> <property name="propagate_natural_width">True</property> <child> <object class="GtkTreeView"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="model">treefilter</property> <child internal-child="selection"> <object class="GtkTreeSelection"/> </child> <child> <object class="GtkTreeViewColumn"> <property name="title" translatable="yes">Sorte</property> <child> <object class="GtkCellRendererText"/> <attributes> <attribute name="text">0</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn"> <property name="title" translatable="yes">Art</property> <child> <object class="GtkCellRendererText"/> <attributes> <attribute name="text">1</attribute> </attributes> </child> </object> </child> </object> </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButtonBox" id="buttonbox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="layout_style">start</property> <child> <placeholder/> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="padding">5</property> <property name="pack_type">end</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> </object> </child> <child type="titlebar"> <placeholder/> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk class Handler: def on_window_destroy(self, *args): Gtk.main_quit() def on_button_clicked(self, widget): x.color = widget.get_label() x.obj("treefilter").refilter() def on_cellrenderer_note_edited(self, widget, row, edit): model, pos = x.obj("selection").get_selected() conv_iter = x.obj("treesort").convert_iter_to_child_iter(pos) x.obj("store").set_value(conv_iter,3,edit) class Example: def __init__(self): self.builder = Gtk.Builder() self.builder.add_from_file("12_treestore.glade") self.builder.connect_signals(Handler()) self.obj = self.builder.get_object #read data from text file with open("12_capsicum.txt") as f: data = [line.strip("\n") for line in f] colors = set() species = set() varieties = [] for line in data: variety = line.split(";") colors.add(variety[2]) species.add(variety[1]) try: variety[3] except IndexError: variety.append("") varieties.append(variety) #append lines to 1st treestore for s in species: counter = 0 row = self.obj("store").append(None, [None, None, None, None, 800, None], ) for v in varieties: if v[1] == s: self.obj("store").append(row, [v[0], v[1], v[2], v[3], 400, None], ) counter += 1 self.obj("store").set_value(row, 0, "{} ({})".format(s, counter)) self.obj("store").set_value(row, 5, counter) #append lines to 2nd treestore [self.obj("filterstore").append(None, [v[0], v[1], v[2]]) for v in varieties] #create buttons in buttonbox for c in colors: button = Gtk.Button.new_with_label(c) button.connect("clicked",Handler().on_button_clicked) self.obj("buttonbox").add(button) self.obj("view").expand_all() self.obj("treesort").set_sort_column_id(5, Gtk.SortType.DESCENDING) self.obj("treefilter").set_visible_func(self.color_filter_func) self.obj("window").show_all() def color_filter_func(self, model, iter, data): if model[iter][2] == self.color: return True else: return False def main(self): Gtk.main() x = Example() x.main()
New project: Knights of Ni
Knights of Ni - small managing tool for the static website generator Nikola

New little project made of Glade and Python on GitHub: Knights of Ni.
Exterminate!
The VTE terminal widget

Glade
The widget can be found in the lower part of the widget side bar and provides a complete terminal emulator. To close the window on the exit
command the child-exited signal has to be assigned.
A button click shall open a Python prompt within the terminal window, so we need the familiar clicked signal.
Python
Elements used in Glade that are not part of the Gtk module have to be registered as a GObject object (this is also required when using a GtkSourceView widget as the functionality is provided by the GtkSource module):
GObject.type_register(Vte.Terminal)
The terminal emulator is initiated by calling spawn_sync
expecting 7 parameters. Detailed information on the parameters are available in the documentation but for a common start a lot of defaults and Nones will do:
terminal.spawn_sync( Vte.PtyFlags.DEFAULT, None, ["/bin/bash"], None, GLib.SpawnFlags.DEFAULT, None, None, )
The feed_child
function must be called to send a command to the console. The expected parameters are the string including a newline and the length of the string:
command = "python\n" x.terminal.feed_child(command,len(command))
Listings
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <requires lib="vte-2.91" version="0.46"/> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="VteTerminal" id="term"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="has_focus">True</property> <property name="hscroll_policy">natural</property> <property name="vscroll_policy">natural</property> <property name="encoding">UTF-8</property> <property name="scroll_on_keystroke">True</property> <property name="scroll_on_output">False</property> <signal name="child-exited" handler="on_window_destroy" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="button"> <property name="label" translatable="yes">start python console</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_button_clicked" swapped="no"/> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">2</property> </packing> </child> </object> </child> <child type="titlebar"> <placeholder/> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import os import gi gi.require_version("Gtk", "3.0") gi.require_version("Vte", "2.91") from gi.repository import Gtk, Vte, GObject, GLib class Handler: def on_window_destroy(self, *args): Gtk.main_quit() def on_button_clicked(self, widget): command = "python\n" x.terminal.feed_child(command.encode()) class Example: def __init__(self): self.builder = Gtk.Builder() GObject.type_register(Vte.Terminal) self.builder.add_from_file("11_terminal.glade") self.builder.connect_signals(Handler()) self.terminal = self.builder.get_object("term") self.terminal.spawn_sync( Vte.PtyFlags.DEFAULT, None, ["/bin/bash"], None, GLib.SpawnFlags.DEFAULT, ) window = self.builder.get_object("window") window.show_all() def main(self): Gtk.main() x = Example() x.main()
Romani ite domum
Contents
Localization with gettext and locale

Glade
Strings in widgets are by default configurated as translatable so there are no preparations required. GetText directly provercesses Glade project files.
Python
Translatable strings
Approved translatable strings are recognized by xgettext
by brackets with a leading underscore:
_ = gettext.gettext translatable_string = _("translate me")
configure (bind)textdomain
Now name and location of the MO files have to be configured in the source code:
locale.bindtextdomain(appname,locales_dir) locale.textdomain(locales_dir) gettext.bindtextdomain(appname,locales_dir) gettext.textdomain(appname) builder.set_translation_domain(appname)
set_translation_domain
has to be called before loading Glade files.
GetText
POT
POT is the abbrevation for Portable Object Template. This file contains all original translatable strings. After generating an empty POT file, xgettext
is executed for all source files containing translatable strings:
$ xgettext --options -o output.pot sourcefile.ext
The identified strings are added to the POT file.
#: sourcefile.ext:line number msgid "translatable string" msgstr ""
The file number reference comment can be avoided by passting the option --no-location
.
In this article's example it is required to run xgettext once for the Glade file and once for the Python source code; the -j
(--join-existing
) option adds new found strings to an existing file:
$ xgettext --sort-output --keyword=translatable --language=Glade -j -o 10_localization/TUT.pot 10_lokalisation.glade $ xgettext --language=Python -j -o 10_localization/TUT.pot 10_lokalisation.py
PO
Translated strings are stored in a PO file per language. A new translation ist invoked by
$ msginit --input=source.pot --locale=xx # xx=language code
that generates a file after the pattern xx.po (p.e. de.po). This file can be edited in any text editor or dedicated tools such like PoEdit. A German localization for example is created by the command
$ msginit --input=TUT.pot --locale=de
If the POT file is altered the PO files are updated with the new strings by executing msgmerge
:
$ msgmerge lang.po template.pot > new_lang.po
MO
MO files are (machine readable) binary files and mandatory for gettext to work. Localization files are located below the bindtextdomain following the file structure path/to/bindtextdomain)/locale/language code/LC_MESSAGES/appname.po
.
In the example the bindtextdomain is created in the local directory, the generated de.po translation text file then transformed into the corresponding MO file:
$ msgfmt --output locale/de/LC_MESSAGES/TUT.mo de.po
Tipps
xgettext options
--no-location
-
Oppress writing line number(s) and file name as comment
--omit-header
-
Avoid overwriting header information
Remove obsolete strings
Strings that are removed from the template remain in the translation files. You can get rid of these by executing this command:
$ msgattrib --set-obsolete --ignore-file=PRJ.pot -o xx.po xx.po
Listings
Glade
10_lokalisation.glade (Source)
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <property name="title" translatable="yes">Localization example (English)</property> <property name="default_width">400</property> <property name="default_height">400</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkMenuBar"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">_File</property> <property name="use_underline">True</property> <child type="submenu"> <object class="GtkMenu"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-new</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-open</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-save</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-save-as</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkSeparatorMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-quit</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> </object> </child> </object> </child> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">_Edit</property> <property name="use_underline">True</property> <child type="submenu"> <object class="GtkMenu"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-cut</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-copy</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-paste</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-delete</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> </object> </child> </object> </child> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">_View</property> <property name="use_underline">True</property> </object> </child> <child> <object class="GtkMenuItem"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">_Help</property> <property name="use_underline">True</property> <child type="submenu"> <object class="GtkMenu"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkImageMenuItem"> <property name="label">gtk-about</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="use_underline">True</property> <property name="use_stock">True</property> </object> </child> </object> </child> </object> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="shadow_type">in</property> <child> <object class="GtkViewport"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkLabel"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">Good morning. In less than an hour, aircraft from here will join others from around the world. And you will be launching the largest aerial battle in this history of mankind. Mankind -- that word should have new meaning for all of us today. We can't be consumed by our petty differences anymore. We will be united in our common interests. Perhaps its fate that today is the 4th of July, and you will once again be fighting for our freedom, not from tyranny, oppression, or persecution -- but from annihilation. We're fighting for our right to live, to exist. And should we win the day, the 4th of July will no longer be known as an American holiday, but as the day when the world declared in one voice: "We will not go quietly into the night! We will not vanish without a fight! We're going to live on! We're going to survive!" Today, we celebrate our Independence Day!</property> <property name="wrap">True</property> </object> </child> </object> </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> </child> <child> <placeholder/> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import gettext import locale import os import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk _ = gettext.gettext class Handler: def on_window_destroy(self, *args): Gtk.main_quit() class Example: def __init__(self): #setting up localization locales_dir = os.path.join(os.getcwd(), "10_localization", "locale", ) appname = "TUT" #required for showing Glade file translations locale.bindtextdomain(appname, locales_dir) locale.textdomain(locales_dir) #required for code translations gettext.bindtextdomain(appname, locales_dir) gettext.textdomain(appname) self.builder = Gtk.Builder() self.builder.set_translation_domain(appname) self.builder.add_from_file("10_lokalisation.glade") self.builder.connect_signals(Handler()) #translatable strings print(_("It's a trap!")) print(_("""These aren't the droids you're looking for.\n""")) #not translatable nonono = """\"Jar Jar is the key to all of this.\"""" george = "...ruined it." print(nonono, george) window = self.builder.get_object("window") window.show_all() def main(self): Gtk.main() x = Example() x.main()
POT
10_localization/TUT.pot (Source)
# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-11-28 13:06+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "" "Good morning. In less than an hour, aircraft from here will join others from " "around the world. And you will be launching the largest aerial battle in " "this history of mankind.\n" "\n" "Mankind -- that word should have new meaning for all of us today.\n" "\n" "We can't be consumed by our petty differences anymore.\n" "\n" "We will be united in our common interests.\n" "\n" "Perhaps its fate that today is the 4th of July, and you will once again be " "fighting for our freedom, not from tyranny, oppression, or persecution -- " "but from annihilation.\n" "\n" "We're fighting for our right to live, to exist.\n" "\n" "And should we win the day, the 4th of July will no longer be known as an " "American holiday, but as the day when the world declared in one voice:\n" "\n" "\"We will not go quietly into the night!\n" "We will not vanish without a fight!\n" "We're going to live on!\n" "We're going to survive!\"\n" "\n" "Today, we celebrate our Independence Day!" msgstr "" msgid "It's a trap!" msgstr "" msgid "Localization example (English)" msgstr "" msgid "These aren't the droids you're looking for.\n" msgstr "" msgid "_Edit" msgstr "" msgid "_File" msgstr "" msgid "_Help" msgstr "" msgid "_View" msgstr ""