© W. David Ashley and Andrew Krause 2019
W. David Ashley and Andrew KrauseFoundations of PyGTK Developmenthttps://doi.org/10.1007/978-1-4842-4179-0_5

5. Basic Widgets

W. David Ashley1  and Andrew Krause2
(1)
AUSTIN, TX, USA
(2)
Leesburg, VA, USA
 

So far, you have not learned about any widgets that are designed to facilitate user interaction—except Gtk.Button . That changes in this chapter, as we cover many types of widgets that allow the user to make choices, change settings, or input information.

These widgets include push buttons, toggle buttons, check buttons, radio buttons, color selection buttons, file chooser buttons, font selection buttons, text entries, and number selection buttons.

In this chapter, you learn
  • How to use clickable buttons with stock items.

  • How to use types of toggle buttons, including check buttons and radio buttons.

  • How to use the entry widget for one-line, free-form text input.

  • How to use the spin button widget for integer or floating-point number selection.

  • What sort of specialized buttons are available.

Using Push Buttons

Previously, this section was titled “Using Stock Items.” But GTK+ 3.x stock items have been deprecated, so I will show you how to create look-alike stock items out of standard items.

Figure 5-1 shows how to create a look-alike stock Close button.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig1_HTML.jpg
Figure 5-1

Look-alike stock button

Use the code in Listing 5-1 to produce the look-alike stock button.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        button = Gtk.Button.new()
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        icon_theme = Gtk.IconTheme.get_default()
        icon = icon_theme.load_icon("window-close", -1,
                                    Gtk.IconLookupFlags.FORCE_SIZE)
        image = Gtk.Image.new_from_pixbuf(icon)
        hbox.add(image)
        label = Gtk.Label.new_with_mnemonic("_Close")
        hbox.add(label)
        hbox.set_homogeneous(True)
        button.add(hbox)
        button.connect("clicked", self.on_button_clicked)
        button.set_relief(Gtk.ReliefStyle.NORMAL)
        self.add(button)
        self.set_size_request(230, 100)
    def on_button_clicked(self, param):
        self.destroy()
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self,
                                    title="Look-alike Stock Item”)
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-1

Look-alike Stock Button

The first task to create a custom button is to make a standard button and then make a horizontal box. The next task is to create an image for the button. The following statements accomplish that task.
icon_theme = Gtk.IconTheme.get_default()
icon = icon_theme.load_icon("window-close", -1,
Gtk.IconLookupFlags.FORCE_SIZE)
image = Gtk.Image.new_from_pixbuf(icon)
hbox.add(image)

The first statement gets the default GTK+ theme. Next we load the PixBuf icon from the theme by name.

Next, we turn the PixBuf icon into an image and then add it to the horizontal box.

Now we create a label an then add it to the horizontal box.
label = Gtk.Label.new_with_mnemonic("_Close")
hbox.add(label)
Now we can connect the button to our custom method, set the relief style for the button, and then add the button to the Gtk.ApplicationWindow.
button.connect("clicked", self.on_button_clicked)
button.set_relief(Gtk.ReliefStyle.NORMAL)
self.add(button)

Tip

The icon image you want may or may not be in the default theme. You may have to look at other themes to find an image you can use. You may need to install a GTK+ theme in order to obtain access to a theme that fits your purpose.

Toggle Buttons

The Gtk.ToggleButton widget is a type of Gtk.Button that holds its active or inactive state after it is clicked. It is shown as pressed down when active. Clicking an active toggle button causes it to return to its normal state. There are two widgets derived from Gtk.ToggleButton: Gtk.CheckButton and Gtk.RadioButton .

You can create a new The Gtk.ToggleButton with one of three functions. To create an empty toggle button, use Gtk.ToggleButton. new() . If you want the toggle button to include a label by default, use Gtk.ToggleButton. new_with_label() . Lastly, Gtk.ToggleButton also supports mnemonic labels with Gtk.ToggleButton. new_with_mnemonic() .

Figure 5-2 shows two Gtk.ToggleButton widgets that were created with two mnemonic labels by calling the Gtk.ToggleButton. new_with_mnemonic() initializer. The widgets in the screenshot were created with the code in Listing 5-2.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig2_HTML.jpg
Figure 5-2

Two Gtk.ToggleButton widgets

In Listing 5-2, when one toggle button is activated, the other is disabled. The only way to make it sensitive is to deactivate the original toggle button.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        toggle1 = Gtk.ToggleButton.new_with_mnemonic("_Deactivate the other one!")
        toggle2 = Gtk.ToggleButton.new_with_mnemonic("_No! Deactivate that one!")
        toggle1.connect("toggled", self.on_button_toggled, toggle2)
        toggle2.connect("toggled", self.on_button_toggled, toggle1)
        vbox.pack_start(toggle1, True, True, 1)
        vbox.pack_start(toggle2, True, True, 1)
        self.add(vbox)
    def on_button_toggled(self, toggle, other_toggle):
        if (Gtk.ToggleButton.get_active(toggle)):
            other_toggle.set_sensitive(False)
            else:
                other_toggle.set_sensitive(True)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Toggle Buttons")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-2

Two Gtk.ToggleButton Widgets

The only signal added by the Gtk.ToggleButton class is "toggled", which is emitted when the user activates or deactivates the button. This signal was triggered in Listing 5-2 by one toggle button in order to disable the other.

In Listing 5-2 another important piece of information was shown: multiple widgets can use the same callback method. We did not need to create a separate callback method for each toggle button, since each required the same functionality. It is also possible to connect one signal to multiple callback methods, although this is not recommended. Instead, you should just implement the whole functionality in a single callback method.

Check Buttons

In most cases, you will not want to use the Gtk.ToggleButton widget, because it looks exactly like a normal Gtk.Button . Instead, GTK+ provides the Gtk.CheckButton widget, which places a discrete toggle next to the display text. Gtk.CheckButton is derived from the Gtk.ToggleButton class. Two instances of this widget are shown in Figure 5-3.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig3_HTML.jpg
Figure 5-3

Two Gtk.CheckButton widgets

As with toggle buttons, three functions are provided for Gtk.CheckButton initialization. These include Gtk.CheckButton. new() , Gtk.CheckButton.new_with_label(), and Gtk.CheckButton. new_with_mnemonic() . Gtk.CheckButton also inherits the important “toggled” signal, which is used in Listing 5-3.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        check1 = Gtk.CheckButton.new_with_label("I am the main option.")
        check2 = Gtk.CheckButton.new_with_label("I rely on the other guy.")
        check2.set_sensitive(False)
        check1.connect("toggled", self.on_button_checked, check2)
        closebutton = Gtk.Button.new_with_mnemonic("_Close")
        closebutton.connect("clicked", self.on_button_close_clicked)
        vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(check1, False, True, 0)
        vbox.pack_start(check2, False, True, 0)
        vbox.pack_start(closebutton, False, True, 0)
        self.add(vbox)
    def on_button_checked(self, check1, check2):
        if check1.get_active():
            check2.set_sensitive(True);
        else:
            check2.set_sensitive(False)
    def on_button_close_clicked(self, button):
        self.destroy()
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
def do_activate(self):
    if not self.window:
        self.window = AppWindow(application=self, title="Check Buttons")
    self.window.show_all()
    self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-3

Gtk.CheckButtons

Excluding the initialization methods, all functionality for check boxes is implemented in the Gtk.ToggleButton class and its ancestors. Gtk.CheckButton is merely a convenience widget, which provides the graphical differences from standard Gtk.Button widgets.

Radio Buttons

The second type of widget derived from Gtk.ToggleButton is the radio button widget. In fact, Gtk.RadioButton is actually derived from Gtk.CheckButton . Radio buttons are toggles that are generally grouped together.

In a group, when one radio button is selected, all others are deselected. The group forbids selecting multiple radio buttons at once. This allows you to provide multiple options to the user where only one should be selected.

Note

GTK+ does not provide a way to deselect a radio button, so one radio button is not desirable. The user is not able to deselect the option! If you only need one button, you should use a Gtk.CheckButton or Gtk.ToggleButton widget.

Radio buttons are drawn as a discrete circular toggle on the side of the label widget, so that they can be differentiated from other types of toggle buttons. It is possible to draw radio buttons with the same toggle as Gtk.CheckButton , but this should not be done because it can confuse and frustrate the user. A group of four radio buttons in a vertical box is shown in Figure 5-4.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig4_HTML.jpg
Figure 5-4

Four Gtk.RadioButton widgets

For radio buttons to work correctly, they must all be referenced to another radio button in the group. Otherwise, all of the buttons would act as independent toggle buttons. An example of how to use multiple radio buttons is shown in Listing 5-4.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        radio1 = Gtk.RadioButton.new_with_label(None, "I want to be clicked!")
        radio2 = Gtk.RadioButton.new_with_label_from_widget(radio1,
                                                    "Click me instead!)
        radio3 = Gtk.RadioButton.new_with_label_from_widget(radio1,
                                                    "No! Click me!”)
        radio4 = Gtk.RadioButton.new_with_label_from_widget(radio3,
                                                    "No! Click me!”)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
        spacing=0) vbox.pack_start(radio1, False, False, 0)
        vbox.pack_start(radio2, False, False, 0)
        vbox.pack_start(radio3, False, False, 0)
        vbox.pack_start(radio4, False, False, 0)
        self.add(vbox)
        self.show_all()
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Radio Buttons")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-4

Gtk.RadioButton

The first radio button in a group can be created with any of the following three functions. However, if you want to use a Gtk.Label widget as the child, it is also possible to use a mnemonic widget, so the toggle can be activated from the keyboard.
radiobutton = Gtk.RadioButton.new(list)
radiobutton = Gtk.RadioButton.new_with_label(list, "My label")
radiobutton = Gtk.RadioButton.new_with_mnemonic(list, "_My label")
However, there is a fourth way to both create multiple radio buttons and a list at the same time. You do this by creating your first radio button without specifying a list. Subsequent radio buttons are created referencing the first radio button created or any other radio button that is a part of the internal group.
radio1 = Gtk.RadioButton.new_with_label(None, "I want to be clicked!")
radio2 = Gtk.RadioButton.new_with_label_from_widget(radio1, "Click me instead!")
radio3 = Gtk.RadioButton.new_with_label_from_widget(radio1, "No! Click me!")
radio4 = Gtk.RadioButton.new_with_label_from_widget(radio3, "No! Click me instead!

None is specified for the radio group in each call. This is because the simplest way to create a group of radio buttons is to associate them to another widget in the group. By using this method, you avoid having to use the GLib with singly linked lists, since the list is created and managed for you automatically.

Referring the initialization function to a radio button that already exists creates each of these. GTK+ adds the new radio button to the group from the specified widget. Because of this, you need only refer to any widget that already exists within the desired radio group.

Lastly, every radio button in the group must be connected to the toggled signal. When a radio button is selected, exactly two radio buttons emit the toggled signal, because one is selected and another is deselected. You will not be able to catch all radio button signals if you do not connect every radio button to toggled.

Text Entries

The Gtk.Entry widget is a single line, free-form text entry widget. It is implemented in a general manner, so that it can be molded to fit many types of solutions. It can be used for text entry, password entry, and even number selections.

Gtk.Entry also implements the Gtk.Editable interface, which provides a large number of functions that are created to handle selections of text. An example Gtk.Entry widget is shown in Figure 5-5. This text entry is used for password entry.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig5_HTML.jpg
Figure 5-5

Gtk.Entry widget for passwords

Note

Gtk.Editable is a special type of object called an interface. An interface is a set of APIs that are implemented by multiple widgets and used for consistency. You learn how to implement and utilize interfaces in your own widgets in Chapter 12.

The Gtk.Entry widget considers all text to be standard strings. The only way it differentiates between normal text and passwords is that a special character called an invisibility character is shown instead of password content. Listing 5-5 shows you how to use a Gtk.Entry widget for password entry. If you want to use a Gtk.Entry widget for normal text entry, you need only to turn visibility on.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import os
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        prompt_str = "What is the password for " + os.getlogin() + "?"
        question = Gtk.Label(prompt_str)
        label = Gtk.Label("Password:")
        passwd = Gtk.Entry()
        passwd.set_visibility(False)
        passwd.set_invisible_char("*")
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        hbox.pack_start(label, False, False, 5)
        hbox.pack_start(passwd, False, False, 5)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(question, False, False, 0)
        vbox.pack_start(hbox, False, False, 0)
        self.add(vbox)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Password")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-5

Gtk.Entry

Entry Properties

The Gtk.Entry widget is a highly flexible widget, because it was designed to be employed in the maximum number of instances. This can be seen from the wide array of properties provided by the class. A sampling of the most important of those is included in this section. For a full list of properties, you should reference Appendix A.

Oftentimes, you want to restrict the length of the free-form text entered into an entry widget because of string limitations of the value. In the following function prototype, entry.set_max_length() limits the text of the entry to a maximum number of characters. This can be useful when you want to limit the length of user names, passwords, or other length-sensitive information.
entry.set_max_length(max_chars)
Invisibility characters facilitate password entries in GTK+. The invisibility character is the character that replace the actual password content in the entry, which can be set with entry. set_invisible_char() . The default character for the entry is an asterisk.
entry.set_invisible_char(single_char)
entry.set_visibility(boolean)

After specifying the invisibility character, you can hide all entered text by setting visibility to False with entry. set_visibility() . You are still able to retrieve the actual content of the entry programmatically, even though it is hidden from view.

Inserting Text into a Gtk.Entry Widget

In GTK+ 3.x there is only one way to replace all the text in a Gtk.Entry widget. The method entry. set_text() overwrites the whole content of the text entry with the given string. However, this is only useful if you no longer care about the current text displayed by the widget.
entry.set_text(text)

The current text displayed by Gtk.Entry can be retrieved with entry.get_text(). This string is used internally by the widget and must never be freed or modified in any way. It is also possible to use entry. insert_text() to insert text into a Gtk.Entry widget. The parameter to entry.insert_text() specify both the text to insert and the character position to insert the text.

Spin Buttons

The Gtk.SpinButton widget is a number selection widget that is capable of handling integers and floating-point numbers. It is derived from Gtk.Entry , so Gtk.SpinButton inherits all of its functions and signals.

Adjustments

Before covering the Gtk.SpinButton widget, you must understand the Gtk.Adjustment class. Gtk.Adjustment is one of the few classes in GTK+ that is not considered a widget, because it is derived directly from Gtk.Object . It is used for several widgets, including spin buttons, view ports, and the multiple widgets derived from Gtk.Range .

New adjustments are created with Gtk.Adjustment. new() . Once added to a widget, memory management of the adjustment is handled by the widget, so you do not have to worry about this aspect of the object.
Gtk.Adjustment.new(initial_value, lower_range, upper_range,
                   step_increment, page_increment, page_size)
New adjustments are initialized with six parameters. A list of these parameters follows.
  • initial_value: The value stored by the adjustment when it is initialized. This corresponds to the value property of the Gtk.Adjustment class.

  • lower_range: The minimum value the adjustment is allowed to hold. This corresponds to the lower property of the Gtk.Adjustment class.

  • lower_range: The maximum value the adjustment is allowed to hold. This corresponds to the upper property of the Gtk.Adjustment class.

  • step_increment: The increment to make the smallest change possible. If you want to count all integers between 1 and 10, the increment would be set to 1.

  • page_increment: The increment to make when Page Up or Page Down is pressed. This is almost always larger than the step_increment.

  • page_size: The size of a page. This value does not have much use in a Gtk.SpinButton , so it should be set to the same value as page_increment or to 0.

There are two useful signals provided by the Gtk.Adjustment class: changed and value-changed. The "changed" signal is emitted when one or more properties of the adjustment have been altered, excluding the value property. The "value-changed" signal is emitted when the current value of the adjustment has been altered.

A Spin Button Example

The spin button widget allows the user to select an integer or floating-point number by incrementing or decrementing with the up or down arrows. The user can still type in a value with the keyboard, and it is displayed as the nearest acceptable value if it is out of range. Figure 5-6 shows two spin buttons in action that display an integer and a floating-point number.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig6_HTML.jpg
Figure 5-6

Spin buttons

Spin buttons show integer or floating-point numbers. In actuality, numbers are stored as double values. The spin button handles rounding the number to the correct number of decimal places. Listing 5-6 is a simple example that creates both integer and floating-point number spin buttons.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        integer = Gtk.Adjustment(5.0, 0.0, 10.0, 1.0, 2.0, 2.0)
        float_pt = Gtk.Adjustment(5.0, 0.0, 1.0, 0.1, 0.5, 0.5)
        spin_int = Gtk.SpinButton()
        spin_int.set_adjustment(integer)
        spin_int.set_increments(1.0, 0)
        spin_int.set_digits(0)
        spin_float = Gtk.SpinButton()
        spin_float.set_adjustment(float_pt)
        spin_float.set_increments(0.1, 0)
        spin_float.set_digits(1)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(spin_int, False, False, 5)
        vbox.pack_start(spin_float, False, False, 5)
        self.add(vbox)
        self.set_size_request(180, 100)
        self.show_all()
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Spin Buttons")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-6

Integer and Floating-Point Number Selection

Before creating the spin buttons, you should create the adjustments. You can also initialize the spin button with a None adjustment, but it is set as insensitive. After your adjustments are initialized, you can create new spin buttons with Gtk.SpinButton. new() . The other two parameters in the initialization function specify the climb rate of the spin button and the number of decimal places to display. The climb rate is how much the value should be incremented or decremented when a (+) or (-) sign is pressed.
Gtk.SpinButton.new(climb_rate, digits)
Alternatively, you can create a new spin button with Gtk.SpinButton. new_with_range() , which automatically creates a new adjustment based on the minimum, maximum, and step values you specify. The initial value is set to the minimum value plus a page increment of ten times the step_increment by default. The precision of the widget is automatically set to the value of step_increment.
Gtk.SpinButton.new_with_range (minimum_value, maximum_value, step_increment)
You can call spinbutton. set_digits() to set a new precision of the spin button and spinbutton.set_value() to set a new value. The value is automatically altered if it is out of bounds of the spin button.
spin_button.set_value(value)

Horizontal and Vertical Scales

Another type of widget called a scale allows you to provide a horizontal or vertical slider that can choose an integer or a floating-point number. Gtk.Scale is both a horizontal scale widget and a vertical scale widget. In GTK+ 2.x the Gtk.Scale was an abstract class. The two subclasses Gtk.HScale and Gtk.VScale were used to create horizontal and vertical scales respectively. In GTK+ 3.x these two classes have been deprecated and the Gtk.Scale has become a real class from which both horizontal and vertical boxes can be created.

The functionality of the Gtk.Scale widget is not much different from Gtk.SpinButton . It is often used when you want to restrict the user from entering values, since the value is chosen by moving the slider. Figure 5-7 shows a screenshot of two horizontal scale widgets.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig7_HTML.jpg
Figure 5-7

Horizontal scale widgets

Scales provide essentially the same functionality as spin buttons, except using a slider chooses the number. To show the similarities between the widgets, Listing 5-7 implements the same functionality as Listing 5-6: two sliders allow the user to select an integer and a floating-point number.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        self.set_size_request(250, -1)
        scale_int = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0.0, 10.0, 1.0)
        scale_float = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0.0, 1.0, 0.1)
        scale_int.set_digits(0)
        scale_float.set_digits(1)
        scale_int.set_value_pos(Gtk.PositionType.RIGHT)
        scale_float.set_value_pos(Gtk.PositionType.LEFT)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(scale_int, False, False, 5)
        vbox.pack_start(scale_float, False, False, 5)
        self.add(vbox)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Scales")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-7

Integer and Floating-Point Number Selection

There are multiple ways to create new scale widgets. The first is with Gtk.Scale. new() , which accepts a Gtk.Adjustment that defines how the scale works.
Gtk.Scale.new(adjustment)
Alternatively, you can create scales with Gtk.Scale. new_with_range() . This function accepts the minimum value, the maximum value, and the step increment of the scale.
Gtk.Scale.new_with_range(minimum, maximum, step)
Since the value of the scale is always stored as a double , you need to define the number of decimal places to show with scale. set_digits() if the default value is not what you want. The default number of decimal places is calculated based on the number of decimal places provided for the step increment. For example, if you provide a step increment of 0.01, two decimal places are displayed by default.
scale.set_digits (digits)
Depending on what type of scale widget you are using, you may want to change where the value is displayed with scale. set_value_pos() . Positions are defined by the Gtk.PositionType enumeration, and they are Gtk.PositionType.LEFT, Gtk.PositionType.RIGHT. Gtk.PositionType.TOP, and Gtk.PositionType.BOTTOM. You can also use scale. set_draw_value() to hide the value from the user’s view altogether.
scale.set_value_pos(pos)

Gtk.Scale is derived from a widget called Gtk.Range . This widget is an abstract type that provides the ability to handle an adjustment. You should use scale. get_value() to retrieve the current value of the scale. Gtk.Range also provides the “value-changed” signal, which is emitted when the user changes the position of the scale.

Gtk.Adjustment widgets may also be shared with other widgets. A single Gtk.Adjustment may be shared with the Gtk.SpinButton and a Gtk.Scale widgets. See the GTK documentation for more information.

Additional Buttons

While the Gtk.Button widget allows you to create your own custom buttons, GTK+ provides three additional button widgets that are at your disposal: the color selection button, file chooser button, and font selection button.

Each of the sections covering these three widgets also cover other important concepts, such as the Gtk.Color class, file filters, and Pango fonts. These concepts are used in later chapters, so it is a good idea to get a grasp of them now.

Color Button

The Gtk.ColorButton widget provides a simple way for you to allow your users to select a specific color. These colors can be specified as six-digit hexadecimal values or the RGB value. The color button itself displays the selected color in a rectangular block set as the child widget of the button. Figure 5-8 is an example of this.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig8_HTML.jpg
Figure 5-8

Color selection dialog

A Gtk.ColorButton Example

When clicked, the color button opens a dialog that allows the user to enter in the color value or browse for a choice on the color wheel. The color wheel is provided so the user is not required to know the numeric values of the colors. Listing 5-8 shows how to use the Gtk.ColorButton widget in an application.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        color = Gdk.RGBA(red=0, green=.33, blue=.66, alpha=1.0)
        color = Gdk.RGBA.to_color(color)
        button = Gtk.ColorButton.new_with_color(color)
        button.set_title("Select a Color!")
        label = Gtk.Label("Look at my color!")
        label.modify_fg(Gtk.StateType.NORMAL, color)
        button.connect("color_set", self.on_color_changed, label)
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        hbox.pack_start(button, False, False, 5)
        hbox.pack_start(label, False, False, 5)
        self.add(hbox)
    def on_color_changed(self, button, label):
        color = button.get_color()
        label.modify_fg(Gtk.StateType.NORMAL, color)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Color Button")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-8

Gtk.ColorButton and Gdk.Color

In most cases, you want to create a Gtk.ColorButton with an initial color value, which is done by specifying a Gdk.Color object to button = Gtk.ColorButton. new_with_color() . The default color, if none is provided, is opaque black with the alpha option disabled.

Storing Colors in Gdk.Color

Gdk.Color is a class that stores red, green, and blue values for a color. These values can be retrieved or set using the method shown next. The fourth available value is the pixel object. It automatically stores the index of the color when it is allocated in a color map, so there is usually no need for you to alter this value.

After creating a new Gdk.Color object, if you already know the red, green, and blue values of the color, you can specify them in the following manner. Red, green, and blue values are stored as unsigned integer values ranging from 0 to 65,535, where 65,535 indicates full-color intensity. For example, the following color refers to white.
mycolorobj = Gdk.Color.new()
mycolorobj.red = 65535
mycolorobj.green = 65535
mycolorobj.blue = 65535

Using the Color Button

After setting your initial color, you can choose the title that is given to the color selection dialog with button. set_title() . By default, the title is “Pick a Color”, so it is not necessary to set this value if you are content with this title.
button.get_color()
label.modify_fg(Gtk.StateType.NORMAL, color)

In Listing 5-8, the foreground color was set in the normal widget state, which is what state all labels are in, by and large, unless they are selectable. There are five options for the Gtk.StateType enumeration that can be used in label. modify_fg() . You can reset the widget’s foreground color to the default value by passing a None color.

File Chooser Buttons

The Gtk.FileChooserButton widget provides an easy method for you to ask users to choose a file or a folder. It implements the functionality of the file selection framework provided by GTK+. Figure 5-9 shows a file chooser button set to select a folder and a button set to select a file.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig9_HTML.jpg
Figure 5-9

File chooser buttons

When the user clicks a Gtk.FileChooserButton , an instance of Gtk.FileChooserDialog is opened that allows the user to browse and select one file or one folder, depending on the type of button you created.

Note

You do not learn how to use the Gtk.FileChooserDialog widget until Chapter 6, but you do not need to directly interface with it at this point, because Gtk.FileChooserButton handles all interactions with the dialog.

A Gtk.FileChooserButton Example

You are able to change basic settings, such as the currently selected file, the current folder, and the title of the file selection window. Listing 5-9 shows you how to use both types of file chooser buttons.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from pathlib import Path
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        label = Gtk.Label("")
        chooser1 = Gtk.FileChooserButton("Choose a Folder.",
                                         Gtk.FileChooserAction.SELECT_FOLDER)
        chooser2 = Gtk.FileChooserButton("Choose a Folder.",
                                         Gtk.FileChooserAction.OPEN)
        chooser1.connect("selection_changed",
                         self.on_folder_changed, chooser2)
        chooser2.connect("selection_changed",
                         self.on_file_changed, label)
        chooser1.set_current_folder(str(Path.home()))
        chooser2.set_current_folder(str(Path.home()))
        filter1 = Gtk.FileFilter()
        filter2 = Gtk.FileFilter()
        filter1.set_name("Image Files")
        filter2.set_name("All Files")
        filter1.add_pattern("*.png")
        filter1.add_pattern("*.jpg")
        filter1.add_pattern("*.gif")
        filter2.add_pattern("*")
        chooser2.add_filter(filter1)
        chooser2.add_filter(filter2)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(chooser1, False, False, 0)
        vbox.pack_start(chooser2, False, False, 0)
        vbox.pack_start(label, False, False, 0)
        self.add(vbox)
        self.set_size_request(240, -1)
    def on_folder_changed(self,
        chooser1, chooser2): folder =
        chooser1.get_filename()
        chooser2.set_current_folder(folder)
    def on_file_changed(self, chooser2, label):
        file = chooser2.get_filename()
        label.set_text(file)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="File Chooser Button")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-9

Using the File Chooser Button

File chooser button widgets are created with Gtk.FileChooserButton. new() . This widget is able to serve two purposes: selecting a single file or a single folder. There are four types of file choosers that can be created (the remaining two are covered in Chapter 6), but file chooser buttons support only Gtk.FileChooserAction .OPEN and Gtk.FileChooserAction.SELECT_FOLDER.

Gtk.FileChooser

The Gtk.FileChooserButton widget is an implementation of the functionality provided by the Gtk.FileChooser class. This means that, while the button is not derived from Gtk.FileChooser, it can still utilize all the methods defined by Gtk.FileChooser. Quite a few of the methods in Listing 5-9 utilize functions provided by Gtk.FileChooser.

In Listing 5-9, chooser1. set_current_folder() was used to set the current folder of each file chooser button to the user’s home directory. The contents of this folder is shown when the user initially clicks a file chooser button unless it is changed through some other means. This method returns True if the folder was successfully changed.
chooser1.set_current_folder(filename)

The Path.home() method is a utility module provided by Python that returns the current user’s home directory. As with most features in pathlib, this method is platform independent.

This brings up a useful characteristic of the file chooser interface; it can be used to browse many types of file structures, whether it is on a UNIX or Windows machine. This is especially useful if you want your application to be designed for multiple operating systems.

Since the file chooser button only allows one file to be selected at a time, you can use chooser1.get_filename()to retrieve the currently selected file or folder, depending on the type of file chooser button. If no file is selected, this function returns None.
filename = chooser1.get_filename()

At this point, you have enough information about the Gtk.FileChooser class to implement file chooser buttons. Gtk.FileChooser is covered in more depth in the next chapter when you learn about the Gtk.FileChooserDialog widget.

File Filters

Gtk.FileFilter objects allow you to restrict the files shown in the file chooser. For example, in Listing 5-9, only PNG, JPG, and GIF files could be viewed and chosen by the user when the Image Files filter was selected.

File filters are created with Gtk.FileFilter. new() . Therefore, you need to use filefilter. set_name() to set a displayed name for the filter type. If you provide more than one filter, this name allows the user to switch between them.
filefilter = Gtk.FileFilter.new ();
filefilter.set_name (name)
Lastly, for a filter to be complete you need to add types of files to show. The standard way of doing this is with filefilter. add_pattern() as shown in the following code snippet. This function allows you to specify a format for the filenames that are to be shown. Usually identifying file extensions that should be shown does this. You can use the asterisk character as a wildcard for any type of filtering function.
filefilter.add_pattern (pattern)

Tip

As in Listing 5-9, you may want to provide an All Files filter that shows every file in the directory. To do this, you should create a filter with only one pattern set to the wildcard character. If you do not provide this filter, the user will never be able to view any files that do not match a pattern provided by another filter.

You can also specify filter patterns with filefilter. add_mime_type() by specifying the Multipurpose Internet Mail Extensions (MIME) type. For example, image/* shows all files that are an image MIME type. The problem with this function is that you need to be familiar with MIME types. However, the advantage of using MIME types is that you do not need to specify every file extension for a filter. It allows you to generalize to all files in a specific MIME category.
filefilter.add_mime_type(mime_type)
After you create the filter, it needs to be added to the file chooser, which can be done with filechooser. add_filter() . Once you supply the filters, the first specified filters is used by default in the file chooser. The user is able to switch between types if you have specified multiple filters.
filechooser.add_filter (filter)

Font Buttons

Gtk.FontButton is another type of specialized button that allows the user to select font parameters that correspond to fonts currently residing on the user’s system. Font options are chosen in a font selection dialog that is displayed when the user clicks the button. These options include the font name, style options, and font size. An example Gtk.FontButton widget is displayed in Figure 5-10.
../images/142357_2_En_5_Chapter/142357_2_En_5_Fig10_HTML.jpg
Figure 5-10

Font selection buttons

Font button widgets are initialized with Gtk.FontButton. new_with_font() , which allows you to specify the initial font. The font is provided as a string in the following format: Family Style Size. Each of the parameters is optional; the default font for Gtk.FontButton is Sans 12, which provides no style parameters.

“Family” refers to the formal font name, such as Sans, Serif, or Arial. Style options can vary between fonts, but they normally include Italic, Bold, and Bold Italic. If you choose a Regular font style, no font style is specified. The size is the point size of the text, such as 12 or 12.5.

A Gtk.FontButton Example

Listing 5-10 creates a Gtk.FontButton widget that is initialized with a Sans Bold 12 font. When the chosen font in the button is changed, the new font is applied to a Gtk.Label widget packed below the font button.
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Pango
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(10)
        label = Gtk.Label("Look at the font!")
        initial_font = Pango.font_description_from_string("Sans Bold 12")
        label.modify_font(initial_font)
        button = Gtk.FontButton.new_with_font("Sans Bold 12")
        button.set_title("Choose a Font")
        button.connect("font_set", self.on_font_changed, label)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(label, False, False, 0)
        self.add(vbox)
    def on_font_changed(self, button, label):
        font = button.get_font()
        desc = Pango.font_description_from_string(font)
        buffer = "Font: " + font
        label.set_text(buffer)
        label.modify_font(desc)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Font Button")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 5-10

Using the Font Button

Using Font Selection Buttons

The code in Listing 5-10 gives the first sampling of the Pango.FontDescription class that you have run across. The Pango.FontDescription class is used to parse font style strings. You can create and use a new font description from a font string, such as Sans Bold 12, by calling Pango. font_description_from_string() as follows.
initial_font = Pango.font_description_from_string("Sans Bold 12")
label.modify_font(initial_font)

After creating a font description, modify_font() can be called to set the font of the widget’s text. This function edits the font description object stored by the widget’s Gtk.StyleContext property.

In Listing 5-10, the label’s text was set to the font stored by the Gtk.FontButton when the “font-set” signal was emitted. You can retrieve the whole font description string stored by the font button with fontbutton. get_font_name() , which was used to retrieve the font string displayed by the label.
fontbutton.get_font_name()
In Listing 5-10, the new font style was applied to the Gtk.Label . However, if you set fontbutton. set_use_font() and fontbutton. set_use_size() to True, the font button uses the font family and size when rendering its text. This allows the user to preview the text in the font button. This is turned off for font buttons by default.
fontbutton.set_use_font(boolean)
fontbutton.set_use_size(boolean)

Test Your Understanding

In this chapter, you learned about a number of basic widgets, such as Gtk.Entry , Gtk.SpinButton , and various types of toggles and buttons. In the following two exercises, you are creating two applications to practice using these widgets.

Exercise 1: Renaming Files

In this exercise, use a Gtk.FileChooserButton widget to allow the user to choose a file on the system. Next, use a Gtk.Entry widget that allows the user to specify a new name for the file. (Note that you can find functions for the file utilities required by this exercise in the Python documentation.)

If the file was successfully renamed, you should disable the Gtk.Entry widget and button until the user chooses a new file. If the user does not have permission to rename the file that is selected, then the Gtk.Entry widget and button should be set as insensitive as well. When you complete this exercise, you can find the solution in Appendix D.

This exercise makes use of two widgets covered in this chapter: Gtk.Entry and Gtk.FileChooserButton . It also requires you to use multiple utility functions provided by Python, including functions to rename a file and retrieve information about the permissions of an existing file.

Although you are not learning about any Python file functions, you may also want to experiment with some other file-related utility functions, such as the ability to create directories, change file permissions, and move throughout a directory structure. Python provides a lot of functionality, and it is worthwhile to explore the API documentation in your free time.

Exercise 2: Spin Buttons and Scales

In this exercise, create three widgets: a spin button, a horizontal scale, and a check button. The spin button and horizontal scale should be set with the same initial value and bounds. If the check button is selected, the two adjustment widgets should be synchronized to the same value. This means that when the user changes the value of one widget, the other is changed to the same value.

Since both widgets support integers and floating-point numbers, you should implement this exercise with various numbers of decimal places. You should also practice creating spin buttons and scales both with adjustments and by using the convenience initializers.

Summary

In this chapter, you have learned about the following nine new widgets that provide you with a meaningful way to interact with your users.
  • Gtk.ToggleButton : A type of Gtk.Button widget that holds its active or inactive state after it is clicked. It is shown as pressed down when it is active.

  • Gtk.CheckButton : Derived from Gtk.ToggleButton , this widget is drawn as a discrete toggle next to the displayed text. This allows it to be differentiated from a Gtk.Button.

  • Gtk.RadioButton : You can group multiple radio button widgets together so that only one toggle in the group can be activated at once.

  • Gtk.Entry : This widget allows the user to enter free-form text on a single line. It also facilitates password entry.

  • Gtk.SpinButton : Derived from Gtk.Entry , spin buttons allow the user to select or enter an integer or floating-point number within a predefined range.

  • Gtk.Scale : Similar to the spin button, this widget allows the user to select an integer or floating-point number by moving a vertical or horizontal slider.

  • Gtk.ColorButton : This special type of button allows the user to select a specific color along with an optional alpha value.

  • Gtk.FileChooserButton : This special type of button allows the user to select a single file or folder that already exists on the system.

  • Gtk.FontButton : This special type of button allows the user to select a font family, style, and size.

In the next chapter, you learn how to create your own custom dialogs using the Gtk.Dialog class and about a number of dialogs that are built into GTK+. By the end of Chapter 6, you have a decent grasp of the most important simple widgets available to you in GTK+. From there, we continue on to more complex topics.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.85.181