Homepage › Forums › Articles › Programming › Developer Tools › Glade
This topic was published by DevynCJohnson and viewed 4092 times since "". The last page revision was "".
- AuthorPosts
Glade is a popular WYSIWYG for GTK interfaces. Glade generates an XML file with the extension "*.glade". Developers can then use special bindings/libraries in their code that allows the program to load the GUI that is defined in the *.glade file. However, before application developers can get that far in their design, they need to understand Glade and the various widgets that can be placed on a window.
Glade is the GUI designer for GTK2 and GTK3. GTK2 is no longer developed, but due to its stability and the large usage, many developers still use GTK2. Thus, Glade v3.8 is for GTK2. To create GTK3 windows, use the latest version of Glade (version 3.16 at the time of writing this article). Glade only offers tools for designing the GUI by placing graphical objects. In other words, there is no ability for coding. Instead, programmers design the window and then save the results to a file. Then, programmers open their preferred IDE and write code using the chosen language. In the code, use the command in the GTK binding for your language to import/load the XML code that defines the created window.
For example, when I code, I open my preferred IDE (Geany) and write Python3 code. The language binding used by Python3 for GTK3 is PyGObject which is in the "gi.repository" module. Linux users can obtain this binding from the "python3-gi" package. So, after installing the binding, I import GTK by using "from gi.repository import Gtk". Then, to use my window, I use the "add_from_file()" command to add the contents of the specified *.glade file or "add_from_string()" if I copied the XML code into the Python script as a variable/constant.
#!/usr/bin/env python3 from gi.repository import Gtk gtkwindow = Gtk.Builder() gtkwindow.add_from_file('/path/to/glade/file.glade') # Some code to modify the window and provide the # desired functionality and such # Alternately, _GUI = ( 'XML code' 'many lines' ) from gi.repository import Gtk gtkwindow = Gtk.Builder() gtkwindow.add_from_string(buffer=_GUI) # "buffer=" in the parameter above fixes a minor Cxfreeze3 bug # Various code here
Various languages may have a slightly different way of utilizing the language binding and loading the Glade code for the window. The GTK commands that manipulate the window or perform some GTK-related task are the same in all (or nearly all languages). However, the GTK command will use the syntax of your chosen language.
Now, to explain the various tools and widgets in Glade. The interface is simple; by default, the widgets are on the left, the window is in the center, and the various tweaks and settings are on the right. In the "widget toolbox" (containing the available widgets) organizes the widgets into groups.
- Actions – These widgets trigger an action like opening the help/about window.
- Toplevels - This contains various windows. Some are ready-made windows like the "font chooser dialog", "color chooser dialog", etc. The "Application Window" widget is commonly used to design a custom window. Most developers will want to start with this widget.
- Containers - After selecting a toplevel, containers are added to customize the window for various layouts and appearances. For example, if a scroll bar or tabs are desired, add them before adding buttons or text.
- Control and Display - The labels (text), buttons, menus, etc. belong to this category.
- GTK+ Unix Print Toplevels - These are toplevels (windows) specifically for printing.
- Deprecated - Deprecated widgets are found here. Try not to use them since they are obsolete widgets.
- Miscellaneous - Various widgets not belonging in other categories go here like Tree-view.
- Composite - The widgets with more functionality and "sub-widgets" belong here like the font, file, and color choosers.
NOTE: If you are using Glade3 (version 3.16 or above) and you do not have some of the widget categories that I have mentioned, then you must install some bindings and developer libraries. I have many developer libraries installed on my system, so I have more than a default installation would contain.
To use Glade, first select a toplevel. Then, place the desired containers. Use the "box" and "grid" widgets to divide the window into multiple sections. If a scrollable window is desired, place a "scrolled window" widget on top of a "viewport" widget. Inside of the scrolled window widget, place the "textview" widget. Then, in your code, use the needed commands to load text into the "textview". The best way to better understand Glade and the widgets is to experiment with the various tools. Thankfully, clicking the "Preview Snapshot" allows developers to see a preview of their window.
The "Properties Dock" allows developers to change the various attributes of widgets like size, appearance, etc. The "Signals" tab is useful for providing an interface between the GUI and the code. For instance, select a button that you wish to be the close button. Then, go to the "signals" tab and look for the signal named "clicked" under "GtkButton". For "Handler", type the name of the function (from your code) that will execute when the close button is clicked. Under "user data", select the name of the window (or other desired object). The selected object will be passed to the function. For example, the function can close the window by using the "destroy()" command and/or "Gtk.main_quit()".
Passing data that is entered in a text box or the value of a check-box are passed to the programs code in a similar way. When configuring the signals of a button, type the name of the function under "Handler", and then (under "User data") select the menu, text-box, radio-button, etc. that will be passed to the function. Then, in the function's code, use a command to get the data from the widget. For example, the "get_text()" command in PyGObject would get the current text from the specified text box.
- "General" offers various options that are particular for that widget type.
- "Packing" allows developers to control the way the widget is placed on a container. For instance, increasing the "height" of a button that is placed on "grid" will make the button span multiple rows.
- "Common" offers settings that are the same across all widgets like the spacing, tool-tip, resizing, etc.
- "Accessibility" controls various accessibility features.
Widgets
The "grid" widget is used to create a container made up of columns and rows while the "box" is one column and multiple rows. "viewport" is one row and one column and is used when the contained widget needs to be placed in a scrollable window. "Notebook" is a container providing tabs. Clicking on a tab while in Glade allows the developer to design the contents if the selected tab.
Radio buttons are toggled and only one radio-button in a group may be active. Check buttons allow users to check/uncheck zero, one, or more boxes in the group.
A "Label" is text that is a widget that displays the desired text. A "Link button" is a label that offers a hyperlink.
A "Button" can be pressed to trigger an action and remains in one state. The "Toggle button" exists as either pressed or unpressed. Each state keeps some condition active/true. Changing the button's state may stop the previous action and activate another state.
The "Progress Bar" shows the current process based on the fullness of a bar. A "Level Bar" completes the same action, but the bar is filled with blocks on each step. The "Spinner" can be used to show that the program is loading, processing data, or still active (not locked-up or frozen).
Many other widgets are available, but these are the ones many newbies should understand.
Sample Code
Below is code for a GTK program that is used to create xdg-desktop files.
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim:fileencoding=utf-8 """@brief Create Desktop Entry Files @file desktop-entry-maker @author Devyn Collier Johnson <[email protected]> @copyright LGPLv3 @version 2016.03.22 @section DESCRIPTION - http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html - http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html @section LICENSE GNU Lesser General Public License v3 Copyright (c) Devyn Collier Johnson, All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. """ # pylint: disable=C0103,E1101 import stat from os import chmod, stat as osstat from gi.repository import Gtk _GUI = """<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.18.3 Copyright (C) LGPLv3 This file is part of Desktop-Entry-Maker. Desktop-Entry-Maker is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Desktop-Entry-Maker is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Desktop-Entry-Maker. If not, see <http://www.gnu.org/licenses/>. Author: Devyn Collier Johnson --> <interface> <requires lib="gtk+" version="3.12"/> <!-- interface-license-type lgplv3 --> <!-- interface-name Desktop-Entry-Maker --> <!-- interface-description Create Desktop Entry Files --> <!-- interface-copyright LGPLv3 --> <!-- interface-authors Devyn Collier Johnson --> <object class="GtkApplicationWindow" id="desktop_entry_window"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title" translatable="yes">Desktop-Entry Maker</property> <property name="default_width">600</property> <property name="default_height">600</property> <property name="icon_name">applications-development</property> <property name="show_menubar">False</property> <signal name="damage-event" handler="_winexit" swapped="no"/> <signal name="delete-event" handler="_winexit" swapped="no"/> <signal name="destroy" handler="_winexit" swapped="no"/> <signal name="destroy-event" handler="_winexit" swapped="no"/> <child> <object class="GtkBox" id="box1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="margin_left">5</property> <property name="margin_right">5</property> <property name="margin_top">5</property> <property name="margin_bottom">5</property> <property name="orientation">vertical</property> <property name="spacing">5</property> <child> <object class="GtkGrid" id="entry_grid"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="column_spacing">5</property> <property name="row_homogeneous">True</property> <child> <object class="GtkLabel" id="label_name"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Application's Name (Required)</property> <property name="label" translatable="yes">Name</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">0</property> </packing> </child> <child> <object class="GtkEntry" id="entry_name"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Application's Name (Required)</property> <property name="hexpand">True</property> <property name="primary_icon_tooltip_text" translatable="yes">The program's name (will be seen in menus)</property> <property name="placeholder_text" translatable="yes">*Required*</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">0</property> </packing> </child> <child> <object class="GtkLabel" id="label_execute"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Command or path to executable (Required) Arguments %f - Single file %F - File list %u - Single URL %U - URL List</property> <property name="label" translatable="yes">Execute</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">2</property> </packing> </child> <child> <object class="GtkEntry" id="entry_execute"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Command or path to executable (Required) Arguments %f - Single file %F - File list %u - Single URL %U - URL List</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">*Required*</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">2</property> </packing> </child> <child> <object class="GtkLabel" id="label_genericname"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Used to indicate the application's general purpose Example: LibreOffice = Office Suite</property> <property name="label" translatable="yes">Generic Name</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">1</property> </packing> </child> <child> <object class="GtkEntry" id="entry_genericname"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Used to indicate the application's general purpose Example: LibreOffice = Office Suite</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">Office Suite</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">1</property> </packing> </child> <child> <object class="GtkLabel" id="label_version"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Application's version number (optional)</property> <property name="label" translatable="yes">Version</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">3</property> </packing> </child> <child> <object class="GtkEntry" id="entry_version"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Application's version number (optional)</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">1.0</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">3</property> </packing> </child> <child> <object class="GtkLabel" id="label_comment"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Tooltip comment (optional)</property> <property name="label" translatable="yes">Comment</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">4</property> </packing> </child> <child> <object class="GtkEntry" id="entry_comment"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Tooltip comment (optional)</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">Use a descriptive comment</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">4</property> </packing> </child> <child> <object class="GtkLabel" id="label_category"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Application categories (separated with a semicolon) Examples: Development Education Game GTK Java Office Settings System Utility</property> <property name="label" translatable="yes">Categories</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">7</property> </packing> </child> <child> <object class="GtkEntry" id="entry_category"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Application categories (separated with a semicolon) Examples: Development Education Game GTK Java Office Settings System Utility</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">System;Development;</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">7</property> </packing> </child> <child> <object class="GtkLabel" id="label_nodisplay"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Do not add application to "open with" and desktop/application menus</property> <property name="label" translatable="yes">NoDisplay</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">9</property> </packing> </child> <child> <object class="GtkCheckButton" id="check_nodisplay"> <property name="label" translatable="yes">True</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Do not add application to "open with" and desktop/application menus</property> <property name="halign">center</property> <property name="xalign">0</property> <property name="draw_indicator">True</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">9</property> </packing> </child> <child> <object class="GtkLabel" id="label_terminal"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Run in a terminal?</property> <property name="label" translatable="yes">Terminal</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">10</property> </packing> </child> <child> <object class="GtkCheckButton" id="check_terminal"> <property name="label" translatable="yes">True</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Run in a terminal?</property> <property name="halign">center</property> <property name="xalign">0</property> <property name="draw_indicator">True</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">10</property> </packing> </child> <child> <object class="GtkLabel" id="label_type"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Entry Type such as "Application", "Link", or "Directory" (Required)</property> <property name="label" translatable="yes">Type</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">8</property> </packing> </child> <child> <object class="GtkComboBoxText" id="combobox_type"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Entry Type such as "Application", "Link", or "Directory" (Required)</property> <property name="hexpand">True</property> <property name="active">0</property> <items> <item translatable="yes">Application</item> <item translatable="yes">Link</item> <item translatable="yes">Directory</item> </items> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">8</property> </packing> </child> <child> <object class="GtkLabel" id="label_icon"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Icon path or icon name (optional) $HOME/.icons $XDG_DATA_DIRS/icons /usr/share/pixmaps</property> <property name="label" translatable="yes">Icon</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">6</property> </packing> </child> <child> <object class="GtkEntry" id="entry_icon"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Icon path or icon name (optional) $HOME/.icons $XDG_DATA_DIRS/icons /usr/share/pixmaps</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">Name or Path (/usr/share/icons/)</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">6</property> </packing> </child> <child> <object class="GtkLabel" id="label_keywords"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes">Keywords for searching (optional) Separate words with a semicolon</property> <property name="label" translatable="yes">Keywords</property> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">5</property> </packing> </child> <child> <object class="GtkEntry" id="entry_keyword"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="tooltip_text" translatable="yes">Keywords for searching (optional) Separate words with a semicolon</property> <property name="hexpand">True</property> <property name="placeholder_text" translatable="yes">editor;cpu;programming;</property> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">5</property> </packing> </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSeparator" id="separator1"> <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="padding">5</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkGrid" id="btn_grid"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="column_homogeneous">True</property> <child> <object class="GtkButton" id="btn_close"> <property name="label">gtk-close</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="tooltip_text" translatable="yes">Close this application</property> <property name="halign">center</property> <property name="use_stock">True</property> <property name="image_position">right</property> <property name="always_show_image">True</property> <signal name="clicked" handler="_winexit" object="desktop_entry_window" swapped="no"/> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">0</property> </packing> </child> <child> <object class="GtkButton" id="_save_as"> <property name="label">gtk-save-as</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="tooltip_text" translatable="yes">Create the Desktop-Entry file in the selected location</property> <property name="halign">center</property> <property name="use_stock">True</property> <property name="always_show_image">True</property> <signal name="clicked" handler="_save_as" object="desktop_entry_window" swapped="no"/> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">0</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="pack_type">end</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkLabel" id="label_about"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">LGPLv3 Devyn Collier Johnson <[email protected]> DCJTech.info Version 2016.01.01</property> <property name="justify">center</property> <property name="selectable">True</property> <property name="track_visited_links">False</property> <attributes> <attribute name="style" value="oblique"/> </attributes> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">3</property> </packing> </child> </object> </child> </object> </interface>""" _FILECHOOSER = """<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.18.3 Copyright (C) LGPLv3 This file is part of File Chooser. File Chooser is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. File Chooser is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with File Chooser. If not, see <http://www.gnu.org/licenses/>. Author: Devyn Collier Johnson --> <interface> <requires lib="gtk+" version="3.12"/> <!-- interface-license-type lgplv3 --> <!-- interface-name File Chooser --> <!-- interface-description Select a file --> <!-- interface-copyright LGPLv3 --> <!-- interface-authors Devyn Collier Johnson --> <object class="GtkApplicationWindow" id="ezwin"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title" translatable="yes">Choose a File</property> <property name="window_position">center-on-parent</property> <property name="default_width">700</property> <property name="default_height">500</property> <property name="destroy_with_parent">True</property> <property name="icon_name">system-file-manager</property> <property name="type_hint">dialog</property> <property name="show_menubar">False</property> <signal name="delete-event" handler="_winexit" object="ezwin" swapped="no"/> <signal name="destroy-event" handler="_winexit" object="ezwin" swapped="no"/> <child> <object class="GtkBox" id="box"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkFileChooserWidget" id="filechooser"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="margin_bottom">5</property> <property name="create_folders">False</property> <property name="preview_widget_active">False</property> <property name="use_preview_label">False</property> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkCheckButton" id="chkbtn2"> <property name="label" translatable="yes">Select current location</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Marked - The current location/folder is returned Empty - The selected folder is returned</property> <property name="halign">center</property> <property name="valign">center</property> <property name="xalign">0</property> <property name="draw_indicator">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> <child> <object class="GtkGrid" id="grid"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="margin_left">5</property> <property name="margin_right">5</property> <property name="margin_top">5</property> <property name="column_homogeneous">True</property> <child> <object class="GtkButton" id="btn1"> <property name="label">gtk-ok</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Submit selection to the program that opened this dialog.</property> <property name="halign">start</property> <property name="valign">center</property> <property name="margin_top">5</property> <property name="margin_bottom">5</property> <property name="use_stock">True</property> <property name="always_show_image">True</property> <signal name="clicked" handler="_select" object="filechooser" swapped="no"/> </object> <packing> <property name="left_attach">0</property> <property name="top_attach">0</property> </packing> </child> <child> <object class="GtkCheckButton" id="chkbtn1"> <property name="label" translatable="yes">View hidden files</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Marked - Show hidden files Empty - Hidden files not seen</property> <property name="halign">center</property> <property name="valign">center</property> <property name="xalign">0</property> <property name="draw_indicator">True</property> <signal name="toggled" handler="_hidden" object="filechooser" swapped="no"/> </object> <packing> <property name="left_attach">1</property> <property name="top_attach">0</property> </packing> </child> <child> <object class="GtkButton" id="btn2"> <property name="label">gtk-cancel</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">False</property> <property name="tooltip_text" translatable="yes">Cancel</property> <property name="halign">end</property> <property name="valign">center</property> <property name="margin_top">5</property> <property name="margin_bottom">5</property> <property name="use_stock">True</property> <property name="image_position">right</property> <property name="always_show_image">True</property> <signal name="clicked" handler="_cancel" object="ezwin" swapped="no"/> </object> <packing> <property name="left_attach">2</property> <property name="top_attach">0</property> </packing> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">4</property> </packing> </child> </object> </child> </object> </interface>""" def write2file(_filename: str, _write) -> None: """Send data to new file or overwrite file""" with open(_filename, mode='wt', encoding='utf-8') as _file: _file.write(str(_write)) return None def ezfilech() -> list or str: """File Chooser Dialog""" _init_path = '' _viewhidden = False _local = True filewin = Gtk.Builder() filewin.add_from_string(buffer=_FILECHOOSER) _filew = filewin.get_object('filechooser') _chkbtn_hidden = filewin.get_object('chkbtn1') _chkbtn_dir = filewin.get_object('chkbtn2') _out = [] _chkbtn_dir.set_visible(False) _filew.set_create_folders(True) _filew.set_local_only(_local) _filew.set_show_hidden(_viewhidden) _chkbtn_hidden.set_active(_viewhidden) if _init_path: try: _filew.set_current_folder(_init_path) except (OSError, NameError, TypeError, ValueError): pass _filew.set_action(Gtk.FileChooserAction.SAVE) _filew.set_do_overwrite_confirmation(True) # Handler Functions def _winexit(*_x) -> None: # pylint: disable=W0613 """Close application""" nonlocal _out _out = 'Exit' Gtk.main_quit() return def _cancel(*_x) -> None: # pylint: disable=W0613 """Cancel and close""" nonlocal _out _out = 'Cancel' Gtk.main_quit() return def _select(*_x) -> None: # pylint: disable=W0613 """Select files""" nonlocal _out _out = ' '.join(_filew.get_filenames()) Gtk.main_quit() return def _hidden(_widget, *_x) -> None: # pylint: disable=W0613 """Show hidden files""" _widget.set_show_hidden(not _widget.get_show_hidden()) return filewin.connect_signals({ '_winexit': _winexit, '_cancel': _cancel, '_select': _select, '_hidden': _hidden, }) Gtk.main() return _out class MainWin(): # pylint: disable=R0903 """Main Window""" def __init__(self): """Main window""" self.ui = Gtk.Builder() self.ui.add_from_string(buffer=_GUI) # Match signal to function (handler) self.ui.connect_signals({ '_winexit': Gtk.main_quit, '_save_as': self._save_as, }) # Buttons def _save_as(self, *_x) -> None: # noqa C901 # pylint: disable=R0912,R0914,R0915,W0613 """Save XDG *.desktop file""" # Get objects entry_name = self.ui.get_object(r'entry_name') entry_genericname = self.ui.get_object(r'entry_genericname') entry_execute = self.ui.get_object(r'entry_execute') entry_icon = self.ui.get_object(r'entry_icon') entry_version = self.ui.get_object(r'entry_version') combobox_type = self.ui.get_object(r'combobox_type') check_terminal = self.ui.get_object(r'check_terminal') check_nodisplay = self.ui.get_object(r'check_nodisplay') entry_comment = self.ui.get_object(r'entry_comment') entry_keyword = self.ui.get_object(r'entry_keyword') entry_category = self.ui.get_object(r'entry_category') # Get Data _entry_name = entry_name.get_text() _entry_genericname = entry_genericname.get_text() _entry_execute = entry_execute.get_text() _entry_icon = entry_icon.get_text() _entry_version = entry_version.get_text() _combobox_type = str(combobox_type.get_active_text()) _check_terminal = str(check_terminal.get_active()).casefold() _check_nodisplay = str(check_nodisplay.get_active()).casefold() _entry_comment = entry_comment.get_text() _entry_keyword = entry_keyword.get_text() _entry_category = entry_category.get_text() if not len(_entry_name) or not len(_entry_execute): return elif not _entry_name[0].isalnum(): return if not len(_entry_version): _entry_version = '1.0' if not len(_entry_comment): _entry_comment = _entry_name if 'none' in _check_terminal.casefold(): _check_terminal = 'false' if 'none' in _check_nodisplay.casefold(): _check_nodisplay = 'false' if 'link' in _combobox_type.casefold(): _entry_type = str( '\nType=Link' + '\nURL=' + _entry_execute ) elif 'directory' in _combobox_type.casefold(): _entry_type = '\nType=Directory' else: _entry_type = str( '\nType=Application' + '\nExec=' + _entry_execute ) # Create Data _data = str( '#!/usr/bin/env xdg-open\n' + '[Desktop Entry]' + '\nName=' + _entry_name + '\nGenericName=' + _entry_genericname + _entry_type + '\nNoDisplay=' + _check_nodisplay + '\nVersion=' + _entry_version + '\nCategories=' + _entry_category + '\nTerminal=' + _check_terminal + '\nIcon=' + _entry_icon + '\nComment=' + _entry_comment + '\nKeywords=' + _entry_keyword + '\n' ) # Select Location _path = ezfilech() if _path.lower() == 'cancel' or _path.lower() == 'none': return if _path == '': return # Write Data try: if _path.endswith('.desktop'): write2file(_path, _data) else: _path = _path + '.desktop' write2file(_path, _data) except Exception: # pylint: disable=W0703 return # Set Permissions _st = osstat(_path) chmod(_path, _st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) # Close/End Gtk.main_quit() return def main() -> None: """Open window and run program""" MainWin() Gtk.main() return if __name__ == '__main__': main()
Further Reading
- GNOME Introspection - https://developer.gnome.org/gi/stable/
- GTK3 - https://developer.gnome.org/gtk3/stable/
- Pygobject - https://developer.gnome.org/pygobject/
- PyGTK - https://developer.gnome.org/pygtk/stable/
- AuthorPosts