commit 5eabe8545ee1d20ed0e84d87d9b018e54267f31b Author: Mark Powers Date: Wed Jan 2 14:53:58 2019 -0500 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/9781484241783.jpg b/9781484241783.jpg new file mode 100644 index 0000000..eb3327c Binary files /dev/null and b/9781484241783.jpg differ diff --git a/App_and_AppWindow/Application1.py b/App_and_AppWindow/Application1.py new file mode 100644 index 0000000..53c4210 --- /dev/null +++ b/App_and_AppWindow/Application1.py @@ -0,0 +1,31 @@ +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(*args, application_id="org.example.myapp", + flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, + **kwargs) + self.window = None + self.add_main_option("test", ord("t"), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, "Command line test", None) + + def do_startup(self): + Gtk.Application.do_startup(self) + action = Gio.SimpleAction.new("quit", None) + action.connect("activate", self.on_quit) + self.add_action(action) + + def do_activate(self): + # We only allow a single window and raise any existing ones + if not self.window: + # Windows are associated with the application + # when the last one is closed the application shuts down + self.window = AppWindow(application=self, title="Main Window") + self.window.present() + + def do_command_line(self, command_line): + options = command_line.get_options_dict() + if options.contains("test"): + # This is printed on the main instance + print("Test argument recieved") + self.activate() + return 0 diff --git a/App_and_AppWindow/Application2.py b/App_and_AppWindow/Application2.py new file mode 100644 index 0000000..bb411a4 --- /dev/null +++ b/App_and_AppWindow/Application2.py @@ -0,0 +1,133 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import GLib, Gio, Gtk + +# This would typically be its own file +MENU_XML=""" + + + +
+ Change label + + win.change_label + String 1 + String 1 + + + win.change_label + String 2 + String 2 + + + win.change_label + String 3 + String 3 + +
+
+ + win.maximize + Maximize + +
+
+ + app.about + _About + + + app.quit + _Quit + <Primary>q + +
+
+
+""" + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # This will be in the windows group and have the "win" prefix + max_action = Gio.SimpleAction.new_stateful("maximize", None, + GLib.Variant.new_boolean(False)) + max_action.connect("change-state", self.on_maximize_toggle) + self.add_action(max_action) + # Keep it in sync with the actual state + self.connect("notify::is-maximized", + lambda obj, pspec: max_action.set_state( + GLib.Variant.new_boolean(obj.props.is_maximized))) + lbl_variant = GLib.Variant.new_string("String 1") + lbl_action = Gio.SimpleAction.new_stateful("change_label", + lbl_variant.get_type(), + lbl_variant) + lbl_action.connect("change-state", self.on_change_label_state) + self.add_action(lbl_action) + self.label = Gtk.Label(label=lbl_variant.get_string(), + margin=30) + self.add(self.label) + + def on_change_label_state(self, action, value): + action.set_state(value) + self.label.set_text(value.get_string()) + + def on_maximize_toggle(self, action, value): + action.set_state(value) + if value.get_boolean(): + self.maximize() + else: + self.unmaximize() + +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(*args, application_id="org.example.myapp", + flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, + **kwargs) + self.window = None + self.add_main_option("test", ord("t"), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, "Command line test", None) + + def do_startup(self): + Gtk.Application.do_startup(self) + action = Gio.SimpleAction.new("about", None) + action.connect("activate", self.on_about) + self.add_action(action) + action = Gio.SimpleAction.new("quit", None) + action.connect("activate", self.on_quit) + self.add_action(action) + builder = Gtk.Builder.new_from_string(MENU_XML, -1) + self.set_app_menu(builder.get_object("app-menu")) + + def do_activate(self): + # We only allow a single window and raise any existing ones + if not self.window: + # Windows are associated with the application + # when the last one is closed the application shuts down + self.window = AppWindow(application=self, title="Main Window") + self.window.present() + + def do_command_line(self, command_line): + options = command_line.get_options_dict() + if options.contains("test"): + # This is printed on the main instance + print("Test argument recieved") + self.activate() + return 0 + + def on_about(self, action, param): + about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True) + about_dialog.present() + + def on_quit(self, action, param): + self.quit() + + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Basic_Widgets/CheckButtons.py b/Basic_Widgets/CheckButtons.py new file mode 100644 index 0000000..44722ec --- /dev/null +++ b/Basic_Widgets/CheckButtons.py @@ -0,0 +1,49 @@ +#!/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) diff --git a/Basic_Widgets/Exercise_1.py b/Basic_Widgets/Exercise_1.py new file mode 100644 index 0000000..6dd7708 --- /dev/null +++ b/Basic_Widgets/Exercise_1.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +import os +from pathlib import Path + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + + rnm = Gtk.Button.new_with_label("Apply") + name = Gtk.Entry.new() + rnm.set_sensitive(False) + name.set_sensitive(False) + + file = Gtk.FileChooserButton("Choose File", Gtk.FileChooserAction.OPEN) + file.set_current_folder(str(Path.home())) + + file.connect("selection-changed", self.on_file_changed, file, rnm, + name) + rnm.connect("clicked", self.on_rename_clicked, file, rnm, name) + + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_start(name, True, True, 0) + hbox.pack_start(rnm, False, True, 0) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + vbox.pack_start(file, False, True, 0) + vbox.pack_start(hbox, False, True, 0) + + self.add(vbox) + + + def on_file_changed(self, chooser, file, rnm, name): + fn = file.get_filename() + mode = os.access(fn, os.W_OK) + + rnm.set_sensitive(mode) + name.set_sensitive(mode) + + def on_rename_clicked(self, chooser, file, rnm, name): + old = file.get_filename() + location = file.get_current_folder() + new = location + "/" + name.get_text() + + os.rename(old, new) + rnm.set_sensitive(False) + name.set_sensitive(False) + +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 Exercise") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Basic_Widgets/Exercise_2.py b/Basic_Widgets/Exercise_2.py new file mode 100644 index 0000000..85f352c --- /dev/null +++ b/Basic_Widgets/Exercise_2.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +import os +from pathlib import Path + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + + adj1 = Gtk.Adjustment.new(0.5, 0.0, 1.0, 0.01, 0.02, 0.02) + adj2 = Gtk.Adjustment.new(0.5, 0.0, 1.02, 0.01, 0.02, 0.02) + + spin = Gtk.SpinButton.new(adj1, 0.01, 2) + scale = Gtk.Scale.new(Gtk.Orientation.HORIZONTAL, adj2) + check = Gtk.CheckButton.new_with_label("Synchronize Spin and Scale") + + check.set_active(True) + scale.set_digits(2) + scale.set_value_pos(Gtk.PositionType.RIGHT) + + spin.connect("value_changed", self.on_spin_value_changed, spin, scale, check) + scale.connect("value_changed", self.on_scale_value_changed, spin, scale, check) + + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5); + vbox.pack_start(spin, False, True, 0) + vbox.pack_start(scale, False, True, 0); + vbox.pack_start(check, False, True, 0); + + self.add(vbox) + + + def on_spin_value_changed(self, widget, spin, scale, check): + val1 = spin.get_value() + val2 = scale.get_value() + + if (check.get_active() and val1 != val2): + if isinstance(widget, Gtk.SpinButton): + scale.set_value(val1) + else: + spin.set_value(val2) + + + +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="Exercise 2") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Basic_Widgets/GtkColorButtonWidget.py b/Basic_Widgets/GtkColorButtonWidget.py new file mode 100644 index 0000000..06d3fc7 --- /dev/null +++ b/Basic_Widgets/GtkColorButtonWidget.py @@ -0,0 +1,45 @@ +#!/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) diff --git a/Basic_Widgets/GtkEntry.py b/Basic_Widgets/GtkEntry.py new file mode 100644 index 0000000..3f9febc --- /dev/null +++ b/Basic_Widgets/GtkEntry.py @@ -0,0 +1,43 @@ +#!/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) diff --git a/Basic_Widgets/GtkFileChooserButtonWidget.py b/Basic_Widgets/GtkFileChooserButtonWidget.py new file mode 100644 index 0000000..66f1a32 --- /dev/null +++ b/Basic_Widgets/GtkFileChooserButtonWidget.py @@ -0,0 +1,64 @@ +#!/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) diff --git a/Basic_Widgets/GtkFontButtonWidget.py b/Basic_Widgets/GtkFontButtonWidget.py new file mode 100644 index 0000000..1b43ed9 --- /dev/null +++ b/Basic_Widgets/GtkFontButtonWidget.py @@ -0,0 +1,47 @@ +#!/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) diff --git a/Basic_Widgets/GtkScaleWidgets.py b/Basic_Widgets/GtkScaleWidgets.py new file mode 100644 index 0000000..dff54ee --- /dev/null +++ b/Basic_Widgets/GtkScaleWidgets.py @@ -0,0 +1,42 @@ +#!/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) diff --git a/Basic_Widgets/GtkSpinButtons.py b/Basic_Widgets/GtkSpinButtons.py new file mode 100644 index 0000000..6a49bee --- /dev/null +++ b/Basic_Widgets/GtkSpinButtons.py @@ -0,0 +1,45 @@ +#!/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) diff --git a/Basic_Widgets/LookAlike_Stock_Button.py b/Basic_Widgets/LookAlike_Stock_Button.py new file mode 100644 index 0000000..7c22cb8 --- /dev/null +++ b/Basic_Widgets/LookAlike_Stock_Button.py @@ -0,0 +1,47 @@ +#!/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) diff --git a/Basic_Widgets/RadioButtons.py b/Basic_Widgets/RadioButtons.py new file mode 100644 index 0000000..ebe0bc2 --- /dev/null +++ b/Basic_Widgets/RadioButtons.py @@ -0,0 +1,40 @@ +#!/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 instead!") + 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) diff --git a/Basic_Widgets/ToggleButtons.py b/Basic_Widgets/ToggleButtons.py new file mode 100644 index 0000000..b99504b --- /dev/null +++ b/Basic_Widgets/ToggleButtons.py @@ -0,0 +1,43 @@ +#!/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) diff --git a/Containers/Adding_Events_To_GtkLabel.py b/Containers/Adding_Events_To_GtkLabel.py new file mode 100644 index 0000000..83ec751 --- /dev/null +++ b/Containers/Adding_Events_To_GtkLabel.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(200, 50) + eventbox = Gtk.EventBox.new() + label = Gtk.Label.new("Double-Click Me!") + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.on_button_pressed, label) + eventbox.add(label) + self.add(eventbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def on_button_pressed(self, eventbox, event, label): + if event.type == Gdk.EventType._2BUTTON_PRESS: + text = label.get_text() + if text[0] == 'D': + label.set_text("I Was Double-Clicked!") + else: + label.set_text("Double-Click Me Again!") + return False + +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="Hello World!") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Container_With_Multiple_Pages.py b/Containers/Container_With_Multiple_Pages.py new file mode 100644 index 0000000..f136dfe --- /dev/null +++ b/Containers/Container_With_Multiple_Pages.py @@ -0,0 +1,40 @@ +#!/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, 100) + notebook = Gtk.Notebook.new() + label1 = Gtk.Label.new("Page 1") + label2 = Gtk.Label.new("Page 2") + child1 = Gtk.Label.new("Go to page 2 to find the answer.") + child2 = Gtk.Label.new("Go to page 1 to find the answer.") + notebook.append_page(child1, label1) + notebook.append_page(child2, label2) + + notebook.set_tab_pos(Gtk.PositionType.BOTTOM) + self.add(notebook) + +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="Notebook") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Exercise_1.py b/Containers/Exercise_1.py new file mode 100644 index 0000000..5973af7 --- /dev/null +++ b/Containers/Exercise_1.py @@ -0,0 +1,79 @@ +#!/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, 150) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + self.add(vbox) + + notebook = Gtk.Notebook.new() + notebook.set_tab_pos(Gtk.PositionType.BOTTOM) + vbox.pack_start(notebook, False, False, 5) + + label1 = Gtk.Label.new("Page 1") + label2 = Gtk.Label.new("Page 2") + label3 = Gtk.Label.new("Page 3") + label4 = Gtk.Label.new("Page 4") + + button1 = Gtk.Button.new_with_label("Go to Page 2") + button1.connect("clicked", self.on_notebook_button_clicked, notebook) + button2 = Gtk.Button.new_with_label("Go to Page 3") + button2.connect("clicked", self.on_notebook_button_clicked, notebook) + button3 = Gtk.Button.new_with_label("Go to Page 4") + button3.connect("clicked", self.on_notebook_button_clicked, notebook) + button4 = Gtk.Button.new_with_label("Go to Page 1") + button4.connect("clicked", self.on_notebook_button_clicked, notebook) + + page1 = notebook.append_page(button1, label1) + page2 = notebook.append_page(button2, label2) + page3 = notebook.append_page(button3, label3) + page4 = notebook.append_page(button4, label4) + + buttonbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL) + vbox.pack_start(buttonbox, False, False, 5) + button_prev = Gtk.Button.new_with_label("Previous Page") + button_prev.connect("clicked", self.on_button_prev_clicked, notebook) + button_close = Gtk.Button.new_with_label("Close") + button_close.connect("clicked", self.on_button_close_clicked) + buttonbox.pack_end(button_prev, False, False, 5) + buttonbox.pack_end(button_close, False, False, 5) + + def on_notebook_button_clicked(self, button, notebook): + nextpage = notebook.props.page + 1 + if nextpage == 4: + nextpage = 0 + notebook.set_current_page(nextpage) + + def on_button_prev_clicked(self, button, notebook): + nextpage = notebook.props.page - 1 + if nextpage == -1: + nextpage = 3 + notebook.set_current_page(nextpage) + + 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="Notebook") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Exercise_2.py b/Containers/Exercise_2.py new file mode 100644 index 0000000..99ba417 --- /dev/null +++ b/Containers/Exercise_2.py @@ -0,0 +1,75 @@ +#!/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, 200) + + notebook = Gtk.Notebook.new() + notebook.set_show_tabs(False) + + for i in range(0, 4): + label = Gtk.Label.new("Tab") + button = Gtk.Button.new_with_mnemonic("_Next Tab") + + expander = Gtk.Expander.new("You Are Viewing Tab %s" % str(i+1)) + expander.set_expanded(True) + expander.add (button) + + notebook.append_page(expander, label) + expander.set_border_width(10) + + button.connect("clicked", self.on_notebook_button_clicked, notebook) + + buttonbox = Gtk.ButtonBox.new(Gtk.Orientation.HORIZONTAL) + button_prev = Gtk.Button.new_with_label("Previous Page") + button_prev.connect("clicked", self.on_button_prev_clicked, notebook) + button_close = Gtk.Button.new_with_label("Close") + button_close.connect("clicked", self.on_button_close_clicked) + buttonbox.pack_end(button_prev, False, False, 5) + buttonbox.pack_end(button_close, False, False, 5) + + paned = Gtk.Paned.new(Gtk.Orientation.VERTICAL) + paned.pack1(notebook, True, False) + paned.pack2(buttonbox, True, False) + + self.add(paned) + + def on_notebook_button_clicked(self, button, notebook): + nextpage = notebook.props.page + 1 + if nextpage == 4: + nextpage = 0 + notebook.set_current_page(nextpage) + + def on_button_prev_clicked(self, button, notebook): + nextpage = notebook.props.page - 1 + if nextpage == -1: + nextpage = 3 + notebook.set_current_page(nextpage) + + 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="Notebook") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Grid_Displaying_Name.py b/Containers/Grid_Displaying_Name.py new file mode 100644 index 0000000..b512c70 --- /dev/null +++ b/Containers/Grid_Displaying_Name.py @@ -0,0 +1,40 @@ +#!/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(150, 100) + grid = Gtk.Grid.new() + label1 = Gtk.Label.new("Enter the following information ...") + label2 = Gtk.Label.new("Name: ") + entry = Gtk.Entry.new() + grid.attach(label1, 0, 0, 2, 1) + grid.attach(label2, 0, 1, 1, 1) + grid.attach(entry, 1, 1, 1, 1) + grid.set_row_spacing(5) + grid.set_column_spacing(5) + self.add(grid) + +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="Tables") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Horizontal_Paned_With_Buttons.py b/Containers/Horizontal_Paned_With_Buttons.py new file mode 100644 index 0000000..522eb1b --- /dev/null +++ b/Containers/Horizontal_Paned_With_Buttons.py @@ -0,0 +1,42 @@ +#!/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) + hpaned = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) + button1 = Gtk.Button.new_with_label("Resize") + button2 = Gtk.Button.new_with_label("Me!") + button1.connect("clicked", self.on_button_clicked) + button2.connect("clicked", self.on_button_clicked) + hpaned.add1(button1) + hpaned.add2(button2) + self.add(hpaned) + self.set_size_request(225, 150) + self.show_all() + + def on_button_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="Panes") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Showing_And_Hiding_Widgets.py b/Containers/Showing_And_Hiding_Widgets.py new file mode 100644 index 0000000..3c50218 --- /dev/null +++ b/Containers/Showing_And_Hiding_Widgets.py @@ -0,0 +1,35 @@ +#!/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(200, 100) + expander = Gtk.Expander.new_with_mnemonic("Click _Me For More!") + label = Gtk.Label.new ("Hide me or show me,\nthat is your choice.") + expander.add(label) + expander.set_expanded(True) + self.add(expander) + +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="Hello World!") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Specifying_Exact_Locations.py b/Containers/Specifying_Exact_Locations.py new file mode 100644 index 0000000..6db3fa6 --- /dev/null +++ b/Containers/Specifying_Exact_Locations.py @@ -0,0 +1,41 @@ +#!/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) + fixed = Gtk.Fixed.new() + button1 = Gtk.Button.new_with_label("Pixel by pixel ...") + button2 = Gtk.Button.new_with_label("you choose my fate.") + button1.connect("clicked", self.on_button_clicked) + button2.connect("clicked", self.on_button_clicked) + fixed.put(button1, 0, 0) + fixed.put(button2, 22, 35) + self.add(fixed) + self.show_all() + + def on_button_clicked(self, widget): + 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="Fixed") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Vertical_Boxes_Specifying_Packing_Parameters.py b/Containers/Vertical_Boxes_Specifying_Packing_Parameters.py new file mode 100644 index 0000000..d1d76f0 --- /dev/null +++ b/Containers/Vertical_Boxes_Specifying_Packing_Parameters.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +names = ["Andrew", "Joe", "Samantha", "Jonathan"] + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + for name in names: + button = Gtk.Button.new_with_label(name) + vbox.pack_end(button, False, False, 5) + button.connect("clicked", self.on_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + self.set_border_width(10) + self.set_size_request(200, -1) + self.add(vbox) + self.show_all() + + def on_button_clicked(self, widget): + 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="Boxes") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Containers/Vertical_Boxes_With_Default_Packing.py b/Containers/Vertical_Boxes_With_Default_Packing.py new file mode 100644 index 0000000..38ff78a --- /dev/null +++ b/Containers/Vertical_Boxes_With_Default_Packing.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +names = ["Andrew", "Joe", "Samantha", "Jonathan"] + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + for name in names: + button = Gtk.Button.new_with_label(name) + vbox.pack_start(button, True, True, 0) + button.connect("clicked", self.on_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + self.set_border_width(10) + self.set_size_request(200, -1) + self.add(vbox) + self.show_all() + + def on_button_clicked(self, widget): + 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="Boxes") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Contributing.md b/Contributing.md new file mode 100644 index 0000000..f6005ad --- /dev/null +++ b/Contributing.md @@ -0,0 +1,14 @@ +# Contributing to Apress Source Code + +Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. + +## How to Contribute + +1. Make sure you have a GitHub account. +2. Fork the repository for the relevant book. +3. Create a new branch on which to make your change, e.g. +`git checkout -b my_code_contribution` +4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. +5. Submit a pull request. + +Thank you for your contribution! \ No newline at end of file diff --git a/Custom_Widgets/Alignment.py b/Custom_Widgets/Alignment.py new file mode 100644 index 0000000..6d6bba1 --- /dev/null +++ b/Custom_Widgets/Alignment.py @@ -0,0 +1,65 @@ +#!/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.resize(300, 100) + # create a grid + grid1 = Gtk.Grid() + grid1.height = 2 + grid1.width = 2 + grid1.set_column_homogeneous(True) + grid1.set_row_homogeneous(True) + self.add(grid1) + # build the aligned labels + label1 = Gtk.Label('Top left Aligned') + label1.can_focus = False + label1.set_halign(Gtk.Align.START) + label1.set_valign(Gtk.Align.START) + grid1.attach(label1, 0, 0, 1, 1) + label2 = Gtk.Label('Top right Aligned') + label2.can_focus = False + label2.set_halign(Gtk.Align.END) + label2.set_valign(Gtk.Align.START) + grid1.attach(label2, 1, 0, 1, 1) + label3 = Gtk.Label('Bottom left Aligned') + label3.can_focus = False + label3.set_halign(Gtk.Align.START) + label3.set_valign(Gtk.Align.END) + grid1.attach(label3, 0, 1, 1, 1) + label4 = Gtk.Label('Bottom right Aligned') + label4.can_focus = False + label4.set_halign(Gtk.Align.END) + label4.set_valign(Gtk.Align.END) + grid1.attach(label4, 1, 1, 1, 1) + +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(*args, application_id="org.example.myapp", + **kwargs) + self.window = None + gtk_version = float(str(Gtk.MAJOR_VERSION)+'.'+str(Gtk.MINOR_VERSION)) + if gtk_version < 3.16: + print('There is a bug in versions of GTK older that 3.16.') + print('Your version is not new enough to prevent this bug from') + print('causing problems in the display of this solution.') + exit(0) + + def do_activate(self): + if not self.window: + self.window = AppWindow(application=self, + title="Alignment") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Custom_Widgets/CustomQuestionDialog.py b/Custom_Widgets/CustomQuestionDialog.py new file mode 100644 index 0000000..8c387c1 --- /dev/null +++ b/Custom_Widgets/CustomQuestionDialog.py @@ -0,0 +1,35 @@ + +class ooQuestionDialog(Gtk.Dialog): + + hbox = None + vbox = None + + def __init__(self, title="Error!", parent=None, + flags=Gtk.DialogFlags.MODAL, + buttons=("NO", Gtk.ResponseType.NO, "_YES", + Gtk.ResponseType.YES)): + super().__init__(title=title, parent=parent, flags=flags, + buttons=buttons) + self.vbox = self.get_content_area() + self.hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, + spacing=5) + icon_theme = Gtk.IconTheme.get_default() + icon = icon_theme.load_icon("dialog-question", 48, + Gtk.IconLookupFlags.FORCE_SVG) + image = Gtk.Image.new_from_pixbuf(icon) + self.hbox.pack_start(image, False, False, 5) + self.vbox.add(self.hbox) + + def set_message(self, message, add_msg=None): + self.hbox.pack_start(Gtk.Label(message), False, False, 5) + if add_msg != None: + expander = Gtk.Expander.new_with_mnemonic( \ + "_Click me for more information.") + expander.add(Gtk.Label(add_msg)) + self.vbox.pack_start(expander, False, False, 10) + + def run(self): + self.show_all() + response = super().run() + self.destroy() + return response diff --git a/Custom_Widgets/ImageLabelButtonClass.py b/Custom_Widgets/ImageLabelButtonClass.py new file mode 100644 index 0000000..dba1864 --- /dev/null +++ b/Custom_Widgets/ImageLabelButtonClass.py @@ -0,0 +1,63 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +class ImageLabelButton(Gtk.Button): + + def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, + image="image-missing", label="Missing", + *args, **kwargs): + super().__init__(*args, **kwargs) + # now set up more properties + hbox = Gtk.Box(orientation, spacing=0) + if not isinstance(image, str): + raise TypeError("Expected str, got %s instead." % str(image)) + icon_theme = Gtk.IconTheme.get_default() + icon = icon_theme.load_icon(image, -1, + Gtk.IconLookupFlags.FORCE_SIZE) + img = Gtk.Image.new_from_pixbuf(icon) + hbox.pack_start(img, True, True, 0) + img.set_halign(Gtk.Align.END) + if not isinstance(label, str): + raise TypeError("Expected str, got %s instead." % str(label)) + if len(label) > 15: + raise ValueError("The length of str may not exceed 15 characters.") + labelwidget = Gtk.Label(label) + hbox.pack_start(labelwidget, True, True, 0) + labelwidget.set_halign(Gtk.Align.START) + self.add(hbox) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(25) + button = ImageLabelButton(image="window-close", label="Close") + button.connect("clicked", self.on_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + self.add(button) + self.set_size_request(170, 50) + + def on_button_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="ImageLabelButton") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Custom_Widgets/Multi_Thread.py b/Custom_Widgets/Multi_Thread.py new file mode 100644 index 0000000..4e200d8 --- /dev/null +++ b/Custom_Widgets/Multi_Thread.py @@ -0,0 +1,162 @@ +#!/usr/bin/python3 + +import sys, threading, queue, time +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + + +def dbsim(q1, q2): + while True: + data = q1.get() + # the request is always the same for our purpose + items = {'lname':"Bunny", 'fname':"Bugs", + 'street':"Termite Terrace", 'city':"Hollywood", + 'state':"California", 'zip':"99999", + 'employer':"Warner Bros.", 'position':"Cartoon character", + 'credits':"Rabbit Hood, Haredevil Hare, What's Up Doc?"} + q2.put(items) + q1.task_done() + + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.lname = None + self.fname = None + self.street = None + self.city = None + self.state = None + self.zip = None + self.employer = None + self.position = None + self.credits = None + self.q1 = queue.Queue() + self.q2 = queue.Queue() + self.thrd = threading.Thread(target=dbsim, daemon=True, args=(self.q1, self.q2)) + self.thrd.start() + + # window setup + self.set_border_width(10) + grid = Gtk.Grid.new() + grid.set_column_spacing(5) + grid.set_row_spacing(5) + # name + label = Gtk.Label.new("Last name:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 0, 0, 1, 1) + self.lname = Gtk.Entry.new() + grid.attach(self.lname, 1, 0, 1, 1) + label = Gtk.Label.new("First name:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 2, 0, 1, 1) + self.fname = Gtk.Entry.new() + grid.attach(self.fname, 3, 0, 1, 1) + # address + label = Gtk.Label.new("Street:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 0, 1, 1, 1) + self.street = Gtk.Entry.new() + grid.attach(self.street, 1, 1, 1, 1) + label = Gtk.Label.new("City:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 2, 1, 1, 1) + self.city = Gtk.Entry.new() + grid.attach(self.city, 3, 1, 1, 1) + label = Gtk.Label.new("State:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 0, 2, 1, 1) + self.state = Gtk.Entry.new() + grid.attach(self.state, 1, 2, 1, 1) + label = Gtk.Label.new("Zip:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 2, 2, 1, 1) + self.zip = Gtk.Entry.new() + grid.attach(self.zip, 3, 2, 1, 1) + # employment status + label = Gtk.Label.new("Employer:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 0, 3, 1, 1) + self.employer = Gtk.Entry.new() + grid.attach(self.employer, 1, 3, 1, 1) + label = Gtk.Label.new("Position:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 2, 3, 1, 1) + self.position = Gtk.Entry.new() + grid.attach(self.position, 3, 3, 1, 1) + label = Gtk.Label.new("Credits:") + label.set_halign(Gtk.Align.END) + grid.attach(label, 0, 4, 1, 1) + self.credits = Gtk.Entry.new() + grid.attach(self.credits, 1, 4, 3, 1) + # buttons + bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) + load_button = Gtk.Button.new_with_label("Load") + bb.pack_end(load_button, False, False, 0) + load_button.connect("clicked", self.on_load_button_clicked) + save_button = Gtk.Button.new_with_label("Save") + bb.pack_end(save_button, False, False, 0) + save_button.connect("clicked", self.on_save_button_clicked) + cancel_button = Gtk.Button.new_with_label("Cancel") + bb.pack_end(cancel_button, False, False, 0) + cancel_button.connect("clicked", self.on_cancel_button_clicked) + # box setup + vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=5) + vbox.add(grid) + vbox.add(bb) + self.add(vbox) + + def on_cancel_button_clicked(self, button): + self.destroy() + + def on_load_button_clicked(self, button): + self.q1.put('request') + # wait for the results to be queued + data = None + while Gtk.events_pending() or data == None: + Gtk.main_iteration() + try: + data = self.q2.get(block=False) + except queue.Empty: + continue + self.lname.set_text(data['lname']) + self.fname.set_text(data['fname']) + self.street.set_text(data['street']) + self.city.set_text(data['city']) + self.state.set_text(data['state']) + self.zip.set_text(data['zip']) + self.employer.set_text(data['employer']) + self.position.set_text(data['position']) + self.credits.set_text(data['credits']) + self.q2.task_done() + + def on_save_button_clicked(self, button): + self.lname.set_text("") + self.fname.set_text("") + self.street.set_text("") + self.city.set_text("") + self.state.set_text("") + self.zip.set_text("") + self.employer.set_text("") + self.position.set_text("") + self.credits.set_text("") + + +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="Multi-Thread") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/ColorSelectionDialog.py b/Dialogs/ColorSelectionDialog.py new file mode 100644 index 0000000..b0371b4 --- /dev/null +++ b/Dialogs/ColorSelectionDialog.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +global_color = Gdk.RGBA(red=.50, green=.50, blue=.50, alpha=1.0).to_color() +global_alpha = 65535 + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(200, 100) + modal = Gtk.Button.new_with_label("Modal") + nonmodal = Gtk.Button.new_with_label("Non-Modal") + modal.connect("clicked", self.on_run_color_selection_dialog, + self, True) + nonmodal.connect("clicked", self.on_run_color_selection_dialog, + self, False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_start(modal, False, False, 5) + hbox.pack_start(nonmodal, False, False, 5) + self.add(hbox) + + def on_dialog_response(self, dialog, result): + if result == Gtk.ResponseType.OK: + colorsel = dialog.get_color_selection() + alpha = colorsel.get_current_alpha() + color = colorsel.get_current_color() + print(color.to_string()) + global_color = color + global_alpha = alpha + dialog.destroy() + + def on_run_color_selection_dialog(self, button, window, domodal): + if domodal: + title = ("Choose Color -- Modal") + else: + title = ("Choose Color -- Non-Modal") + dialog = Gtk.ColorSelectionDialog(title=title, parent=window, modal=domodal) + colorsel = dialog.get_color_selection() + colorsel.set_has_opacity_control(True) + colorsel.set_current_color(global_color) + dialog.connect("response", self.on_dialog_response) + dialog.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="Color Selection Dialog") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/Exercise_1.py b/Dialogs/Exercise_1.py new file mode 100644 index 0000000..d542ac7 --- /dev/null +++ b/Dialogs/Exercise_1.py @@ -0,0 +1,128 @@ +#!/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) + + savebutton = Gtk.Button.new_with_label("Save a File") + createbutton = Gtk.Button.new_with_label("Create a New Folder") + openbutton = Gtk.Button.new_with_label("Open One or More Files") + selectbutton = Gtk.Button.new_with_label("Select a Folder") + + savebutton.connect("clicked", self.on_save_clicked) + createbutton.connect("clicked", self.on_create_clicked) + openbutton.connect("clicked", self.on_open_clicked) + selectbutton.connect("clicked", self.on_select_clicked) + + vbox = Gtk.Box.new (Gtk.Orientation.VERTICAL, 5) + vbox.pack_start(savebutton, False, False, 5) + vbox.pack_start(createbutton, False, False, 5) + vbox.pack_start(openbutton, False, False, 5) + vbox.pack_start(selectbutton, False, False, 5) + + self.add (vbox) + + def on_save_clicked(self, button): + dialog = Gtk.Dialog(title="Save File As ...", + buttons=("Cancel", Gtk.ResponseType.CANCEL, + "Save", Gtk.ResponseType.OK), + parent=self) + dialog.set_border_width(10) + dialog.set_size_request(600, -1) + chooser = Gtk.FileChooserWidget.new(Gtk.FileChooserAction.SAVE) + + dialog.vbox.pack_start(chooser, False, False, 5) + dialog.show_all() + + result = dialog.run() + if result == Gtk.ResponseType.OK: + filename = chooser.get_filename() + print("Saving file as: %s", filename) + + dialog.destroy() + + def on_create_clicked(self, button): + dialog = Gtk.Dialog(title="Create a Folder", + buttons=("Cancel", Gtk.ResponseType.CANCEL, + "Create", Gtk.ResponseType.OK), + parent=self) + dialog.set_border_width(10) + dialog.set_size_request(600, -1) + chooser = Gtk.FileChooserWidget.new(Gtk.FileChooserAction.CREATE_FOLDER) + + dialog.vbox.pack_start(chooser, False, False, 5) + dialog.show_all() + + result = dialog.run() + if result == Gtk.ResponseType.OK: + filename = chooser.get_filename() + print("Creating directory: %s", filename) + + dialog.destroy() + + + def on_open_clicked(self, button): + dialog = Gtk.Dialog(title="Open file(s) ...", + buttons=("Cancel", Gtk.ResponseType.CANCEL, + "Open", Gtk.ResponseType.OK), + parent=self) + dialog.set_border_width(10) + dialog.set_size_request(600, -1) + chooser = Gtk.FileChooserWidget.new(Gtk.FileChooserAction.OPEN) + chooser.set_select_multiple(True) + + dialog.vbox.pack_start(chooser, False, False, 5) + dialog.show_all() + + result = dialog.run() + if result == Gtk.ResponseType.OK: + filenames = chooser.get_filenames() + for filename in filenames: + print("Open file: %s", filename) + + dialog.destroy() + + + def on_select_clicked(self, button): + dialog = Gtk.Dialog(title="Select Folder ...", + buttons=("Cancel", Gtk.ResponseType.CANCEL, + "Select", Gtk.ResponseType.OK), + parent=self) + dialog.set_border_width(10) + dialog.set_size_request(600, -1) + chooser = Gtk.FileChooserWidget.new(Gtk.FileChooserAction.SELECT_FOLDER) + + dialog.vbox.pack_start(chooser, False, False, 5) + dialog.show_all() + + result = dialog.run() + if result == Gtk.ResponseType.OK: + filename = chooser.get_filename() + print("Selected directory: %s", filename) + + dialog.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="Exercise 1") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/FileChooserDialogFolderCreate.py b/Dialogs/FileChooserDialogFolderCreate.py new file mode 100644 index 0000000..8c9119c --- /dev/null +++ b/Dialogs/FileChooserDialogFolderCreate.py @@ -0,0 +1,46 @@ +#!/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(200, 100) + button = Gtk.Button.new_with_label("Create a Folder ...") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + + def on_button_clicked(self, button, parentwin): + dialog = Gtk.FileChooserDialog(title="Create a Folder ...", + parent=parentwin, + action=Gtk.FileChooserAction.SAVE, + buttons=("_Cancel", + Gtk.ResponseType.CANCEL, + "_Ok", Gtk.ResponseType.OK)) + response = dialog.run() + if response == Gtk.ResponseType.OK: + filename = dialog.get_filename(); + print("Creating directory: %s\n" % filename) + dialog.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="Create Folder") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/FileChooserDialogMultipleFiles.py b/Dialogs/FileChooserDialogMultipleFiles.py new file mode 100644 index 0000000..fb2a04e --- /dev/null +++ b/Dialogs/FileChooserDialogMultipleFiles.py @@ -0,0 +1,52 @@ +#!/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(200, 100) + button = Gtk.Button.new_with_label("Open file(s) ...") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + + def on_button_clicked(self, button, parentwin): + dialog = Gtk.FileChooserDialog(title="Open file(s) ...", + parent=parentwin, + action=Gtk.FileChooserAction.OPEN, + buttons=("_Cancel", + Gtk.ResponseType.CANCEL, + "_Open", Gtk.ResponseType.ACCEPT)) + dialog.set_select_multiple(True) + response = dialog.run() + if response == Gtk.ResponseType.ACCEPT: + filenames = dialog.get_filenames() + i = 0 + while i < len(filenames): + file = filenames[i] + print(file + " was selected.") + i += 1 + dialog.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="Open Nultiple Files") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/FileChooserDialog_Saving.py b/Dialogs/FileChooserDialog_Saving.py new file mode 100644 index 0000000..97ac06d --- /dev/null +++ b/Dialogs/FileChooserDialog_Saving.py @@ -0,0 +1,46 @@ +#!/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(200, 100) + button = Gtk.Button.new_with_label("Save as ...") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + + def on_button_clicked(self, button, parentwin): + dialog = Gtk.FileChooserDialog(title="Save file as ...", + parent=parentwin, + action=Gtk.FileChooserAction.SAVE, + buttons=("_Cancel", + Gtk.ResponseType.CANCEL, + "_Save", Gtk.ResponseType.ACCEPT)) + response = dialog.run() + if response == Gtk.ResponseType.ACCEPT: + filename = dialog.get_filename() + button.set_label(filename) + dialog.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="Save a File") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/FontSelectionDialog.py b/Dialogs/FontSelectionDialog.py new file mode 100644 index 0000000..0cc4aaf --- /dev/null +++ b/Dialogs/FontSelectionDialog.py @@ -0,0 +1,58 @@ +#!/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(200, 100) + button = Gtk.Button.new_with_label("Run Font Selection Dialog") + button.connect("clicked", self.on_run_font_selection_dialog) + self.add(button) + + def on_run_font_selection_dialog(self, button): + dialog = Gtk.FontSelectionDialog(title="Choose a Font", + buttons=("Apply", Gtk.ResponseType.APPLY), + parent=self) + dialog.set_preview_text("GTK+ 3 Development With Python") + dialog.connect("response", self.on_dialog_response) +# dialog.show_all() + dialog.run() + + def on_dialog_response(self, dialog, response): + if response == Gtk.ResponseType.OK or response == Gtk.ResponseType.APPLY: + font = dialog.get_font_name() + message = Gtk.MessageDialog(title="Selected Font", + flags=Gtk.DialogFlags.MODAL, + type=Gtk.MessageType.INFO, + text=font, + buttons=("Ok", Gtk.ResponseType.OK), + parent=dialog); + message.run() + message.destroy() + if response == Gtk.ResponseType.OK: + dialog.destroy() + else: + dialog.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="Font Selection Dialog") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/GtkAboutDialogWidget.py b/Dialogs/GtkAboutDialogWidget.py new file mode 100644 index 0000000..4171789 --- /dev/null +++ b/Dialogs/GtkAboutDialogWidget.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GdkPixbuf + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + button = Gtk.Button.new_with_mnemonic("_Click Me") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + self.set_size_request(150, 50) + self.show_all() + + def on_button_clicked(self, button, parent): + authors = ["Author #1", "Author #2"] + documenters = ["Documenter #1", "Documenter #2"] + dialog = Gtk.AboutDialog(parent=parent) + logo = GdkPixbuf.Pixbuf.new_from_file("./logo.png") + if logo != None: + dialog.set_logo(logo) + else: + print("A GdkPixbuf Error has occurred.") + dialog.set_name("Gtk.AboutDialog") + dialog.set_version("3.0") + dialog.set_copyright("(C) 2007 Andrew Krause") + dialog.set_comments("All about Gtk.AboutDialog") + dialog.set_license("Free to all!") + dialog.set_website("http://book.andrewKrause.net") + dialog.set_website_label("book.andrewkrause.net") + dialog.set_authors(authors) + dialog.set_documenters(documenters) + dialog.set_translator_credits("Translator #1\nTranslator #2") + dialog.connect("response", self.on_dialog_button_clicked) + dialog.run() + + def on_dialog_button_clicked(self, dialog, response): + dialog.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="About Dialog") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/GtkAssistant.py b/Dialogs/GtkAssistant.py new file mode 100644 index 0000000..91d02d2 --- /dev/null +++ b/Dialogs/GtkAssistant.py @@ -0,0 +1,128 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +import time + +class assistant(Gtk.Assistant): + progress = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_size_request(450, 300) + self.set_title("Gtk.Assistant Example") + self.connect("destroy", Gtk.main_quit, None) + # create page 0 + page0_widget = Gtk.Label("This in an example of a Gtk.Assistant. By\n" + + "clicking the forward button, you can " + + "continue\nto the next section!") + self.append_page(page0_widget) + self.set_page_title(page0_widget, "Introduction") + self.set_page_type(page0_widget, Gtk.AssistantPageType.INTRO) + self.set_page_complete(page0_widget, True) + # create page 1 + page1_widget = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) + label = Gtk.Label("Your Name: ") + entry = Gtk.Entry() + page1_widget.pack_start(label, False, False, 5) + page1_widget.pack_start(entry, False, False, 5) + self.append_page(page1_widget) + self.set_page_title(page1_widget, "") + self.set_page_type(page1_widget, Gtk.AssistantPageType.CONTENT) + self.set_page_complete(page1_widget, False) + # create page 2 + page2_widget = Gtk.CheckButton.new_with_label("Click me to Continue!") + self.append_page(page2_widget) + self.set_page_title(page2_widget, "Click the Check Button") + self.set_page_type(page2_widget, Gtk.AssistantPageType.CONTENT) + self.set_page_complete(page2_widget, False) + # create page 3 + page3_widget = Gtk.Alignment.new(0.5, 0.5, 0.0, 0.0) + button = Gtk.Button.new_with_label("Click Me!") + self.progress = Gtk.ProgressBar() + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) + hbox.pack_start(self.progress, True, False, 5) + hbox.pack_start(button, False, False, 5) + page3_widget.add(hbox) + self.append_page(page3_widget) + self.set_page_title(page3_widget, "Click the Check Button") + self.set_page_type(page3_widget, Gtk.AssistantPageType.PROGRESS) + self.set_page_complete(page3_widget, False) + # create page 4 + page4_widget = Gtk.Label("Text has been entered in the label and the\n" + + "combo box is clicked. If you are done, then\n" + + "it is time to leave!") + self.append_page(page4_widget) + self.set_page_title(page4_widget, "Confirmation") + self.set_page_type(page4_widget, Gtk.AssistantPageType.CONFIRM) + self.set_page_complete(page4_widget, True) + # set up the callbacks + entry.connect("changed", self.entry_changed) + page2_widget.connect("toggled", self.button_toggled) + button.connect("clicked", self.button_clicked) + self.connect("cancel", self.assistant_canceled) + self.connect("close", self.assistant_close) + + def entry_changed(self, entry): + text = entry.get_text() + num = self.get_current_page() + page = self.get_nth_page(num) + self.set_page_complete(page, len(text) > 0) + + def button_toggled(self, toggle): + active = toggle.get_active() + self.set_page_complete(toggle, active) + + def button_clicked(self, button): + percent = 0.0 + button.set_sensitive(False) + page = self.get_nth_page(3) + while (percent <= 100.0): + message = str(percent) + " complete" + print(message) + self.progress.set_fraction(percent / 100.0) + self.progress.set_text(message) + while (Gtk.events_pending()): + Gtk.main_iteration() + time.sleep(1) + percent += 5.0 + self.set_page_complete(page, True) + + def assistant_canceled(self, response): + self.destroy() + + def assistant_close(self, response): + print("You would apply your changes now!") + self.destroy() + +class AppWindow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(25) + button = Gtk.Button.new_with_mnemonic("_Open Assistant") + button.connect("clicked", self.on_start_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + self.add(button) + self.set_size_request(200, 100) + + def on_start_button_clicked(self, button): + assistant() + +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="Gtk.Assistant") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/GtkMessageDialog.py b/Dialogs/GtkMessageDialog.py new file mode 100644 index 0000000..ccbfa24 --- /dev/null +++ b/Dialogs/GtkMessageDialog.py @@ -0,0 +1,48 @@ +#!/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_with_mnemonic("_Click Me") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + self.set_size_request(150, 50) + + def on_button_clicked(self, button, parent): + dialog = Gtk.Dialog(title="Information", parent=parent, + flags=Gtk.DialogFlags.MODAL) + dialog.add_button("Ok", Gtk.ResponseType.OK) + label = Gtk.Label("The button was clicked.") + image = Gtk.Image.new_from_icon_name("dialog-information", + Gtk.IconSize.DIALOG) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_start(image, False, False, 0) + hbox.pack_start(label, False, False, 0) + dialog.vbox.pack_start(hbox, False, False, 0) + dialog.show_all() + dialog.run() + dialog.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="Dialogs") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/GtkMessageDialogWidget.py b/Dialogs/GtkMessageDialogWidget.py new file mode 100644 index 0000000..a8f80d8 --- /dev/null +++ b/Dialogs/GtkMessageDialogWidget.py @@ -0,0 +1,42 @@ +#!/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_with_mnemonic("_Click Me") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + self.set_size_request(150, 50) + + def on_button_clicked(self, button, parent): + dialog = Gtk.MessageDialog(type=Gtk.MessageType.INFO, parent=parent, + flags=Gtk.DialogFlags.MODAL, + buttons=("Ok", Gtk.ResponseType.OK), + text="The button was clicked.", + title="Information") + dialog.run() + dialog.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="Dialogs") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/SimpleDialog.py b/Dialogs/SimpleDialog.py new file mode 100644 index 0000000..503b74e --- /dev/null +++ b/Dialogs/SimpleDialog.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +import os +import getpass +import socket +import pwd + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + button = Gtk.Button.new_with_mnemonic("_Click Me") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + self.set_size_request(180, 50) + self.show_all() + + def on_button_clicked(self, button, parent): + dialog = Gtk.Dialog(title="Edit User Information", parent=parent, + flags=Gtk.DialogFlags.MODAL) + dialog.add_button("Ok", Gtk.ResponseType.OK) + dialog.add_button("Cancel", Gtk.ResponseType.CANCEL) + dialog.set_default_response(Gtk.ResponseType.OK) + lbl1 = Gtk.Label("User Name:") + lbl2 = Gtk.Label("Real Name:") + lbl3 = Gtk.Label("Home Dir:") + lbl4 = Gtk.Label("Host Name:") + user = Gtk.Entry() + real_name = Gtk.Entry() + home = Gtk.Entry() + host = Gtk.Entry() + user.set_text(getpass.getuser()) + real_name.set_text(pwd.getpwuid(os.getuid())[4]) + home.set_text(os.environ['HOME']) + host.set_text(socket.gethostname()) + grid = Gtk.Grid() + grid.attach(lbl1, 0, 0, 1, 1) + grid.attach(lbl2, 0, 1, 1, 1) + grid.attach(lbl3, 0, 2, 1, 1) + grid.attach(lbl4, 0, 3, 1, 1) + grid.attach(user, 1, 0, 1, 1) + grid.attach(real_name, 1, 1, 1, 1) + grid.attach(home, 1, 2, 1, 1) + grid.attach(host, 1, 3, 1, 1) + dialog.vbox.pack_start(grid, False, False, 5) + dialog.show_all() + result = dialog.run() + if result == Gtk.ResponseType.OK: + print("User Name: " + user.get_text()) + print("Real Name: " + real_name.get_text()) + print("Home: " + home.get_text()) + print("Host: " + host.get_text()) + dialog.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="Simple Dialog") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dialogs/nonmodalGtkMessageDialog.py b/Dialogs/nonmodalGtkMessageDialog.py new file mode 100644 index 0000000..92c2bd9 --- /dev/null +++ b/Dialogs/nonmodalGtkMessageDialog.py @@ -0,0 +1,50 @@ +#!/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_with_mnemonic("_Click Me") + button.connect("clicked", self.on_button_clicked, self) + self.add(button) + self.set_size_request(150, 50) + self.show_all() + + def on_button_clicked(self, button, parent): + dialog = Gtk.Dialog(title="Information", parent=parent) + dialog.add_button("Ok", Gtk.ResponseType.OK) + label = Gtk.Label("The button was clicked.") + image = Gtk.Image.new_from_icon_name("dialog-information", + Gtk.IconSize.DIALOG) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_start(image, False, False, 0) + hbox.pack_start(label, False, False, 0) + dialog.vbox.pack_start(hbox, False, False, 0) + dialog.connect("response", self.on_dialog_button_clicked) + dialog.show_all() + + def on_dialog_button_clicked(self, dialog, response): + dialog.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="Dialogs") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dynamic_User_Interfaces/Exercise_1.glade b/Dynamic_User_Interfaces/Exercise_1.glade new file mode 100644 index 0000000..76f99ba --- /dev/null +++ b/Dynamic_User_Interfaces/Exercise_1.glade @@ -0,0 +1,192 @@ + + + + + + True + False + edit-find + + + 600 + 450 + False + + + + + + True + False + vertical + + + True + False + start + + + True + False + _New + True + document-new + + + + False + True + + + + + True + False + _Open + True + document-open + + + + False + True + + + + + True + False + _Save + True + document-save + + + + False + True + + + + + True + False + + + False + True + + + + + True + False + C_ut + True + edit-cut + + + + False + True + + + + + True + False + _Copy + True + edit-copy + + + + False + True + + + + + True + False + _Paste + True + edit-paste + + + + False + True + + + + + False + True + 0 + + + + + True + True + in + + + True + True + True + True + + + + + False + True + 1 + + + + + True + False + + + 250 + True + True + Search for ... + + + True + True + 0 + + + + + Find + -1 + True + True + True + image1 + + + False + True + 5 + 1 + + + + + False + True + 5 + 2 + + + + + + diff --git a/Dynamic_User_Interfaces/Exercise_1.py b/Dynamic_User_Interfaces/Exercise_1.py new file mode 100644 index 0000000..c3531af --- /dev/null +++ b/Dynamic_User_Interfaces/Exercise_1.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +class SignalHandlers(): + + def on_new_clicked(self, button ): + pass + + def on_open_clicked(self, button ): + pass + + def on_save_clicked(self, button ): + pass + + def on_cut_clicked(self, button ): + pass + + def on_copy_clicked(self, button ): + pass + + def on_paste_clicked(self, button ): + pass + +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: + builder = Gtk.Builder() + builder.add_from_file("./Exercise_1.glade") + self.window = builder.get_object("window") + self.add_window(self.window) + builder.connect_signals(SignalHandlers()) + self.add_window(self.window) + self.window.show_all() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dynamic_User_Interfaces/Exercise_2.glade b/Dynamic_User_Interfaces/Exercise_2.glade new file mode 100644 index 0000000..1a7cc8d --- /dev/null +++ b/Dynamic_User_Interfaces/Exercise_2.glade @@ -0,0 +1,215 @@ + + + + + + True + False + gtk-missing-image + + + True + False + gtk-missing-image + + + True + False + gtk-missing-image + + + True + False + gtk-missing-image + + + True + False + gtk-missing-image + + + True + False + gtk-missing-image + + + True + False + edit-find + + + 600 + 450 + False + + + + + + True + False + vertical + + + True + False + + + True + False + _File + True + + + True + False + + + _New + True + False + True + False + + + + + + _Open + True + False + True + False + + + + + + _Save + True + False + True + False + + + + + + + + + + True + False + _Edit + True + + + True + False + + + C_ut + True + False + True + False + + + + + + _Copy + True + False + True + False + + + + + + _Paste + True + False + True + False + + + + + + + + + + False + True + 0 + + + + + True + True + in + + + True + True + True + True + + + + + False + True + 1 + + + + + True + False + + + 250 + True + True + Search for ... + + + True + True + 0 + + + + + Find + -1 + True + True + True + image1 + + + False + True + 5 + 1 + + + + + False + True + 5 + 2 + + + + + + diff --git a/Dynamic_User_Interfaces/Exercise_2.py b/Dynamic_User_Interfaces/Exercise_2.py new file mode 100644 index 0000000..d9dad5a --- /dev/null +++ b/Dynamic_User_Interfaces/Exercise_2.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +class SignalHandlers(): + + def on_new_clicked(self, button ): + pass + + def on_open_clicked(self, button ): + pass + + def on_save_clicked(self, button ): + pass + + def on_cut_clicked(self, button ): + pass + + def on_copy_clicked(self, button ): + pass + + def on_paste_clicked(self, button ): + pass + +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: + builder = Gtk.Builder() + builder.add_from_file("./Exercise_2.glade") + self.window = builder.get_object("window") + self.add_window(self.window) + builder.connect_signals(SignalHandlers()) + self.add_window(self.window) + self.window.show_all() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Dynamic_User_Interfaces/FileBrowser.glade b/Dynamic_User_Interfaces/FileBrowser.glade new file mode 100644 index 0000000..c8cac80 --- /dev/null +++ b/Dynamic_User_Interfaces/FileBrowser.glade @@ -0,0 +1,253 @@ + + + + + + False + File Browser + + + + + + + True + False + vertical + + + True + False + both + 2 + + + True + False + Back + True + gtk-go-back + + + + False + False + + + + + True + False + Forward + True + gtk-go-forward + + + + False + False + + + + + True + False + Up + True + gtk-go-up + + + + False + False + + + + + True + False + + + False + True + + + + + True + False + Refresh + True + gtk-refresh + + + + False + False + + + + + True + False + Home + True + gtk-home + + + + False + False + + + + + True + False + + + False + True + + + + + True + False + Delete + True + gtk-delete + + + + False + False + + + + + True + False + Information + True + gtk-dialog-info + + + + False + False + + + + + False + True + 0 + + + + + True + False + + + True + False + 5 + 5 + Current Location: + + + False + True + 0 + + + + + True + True + + + + True + True + 1 + + + + + True + True + True + + + + True + False + + + True + False + gtk-jump-to + + + False + True + 0 + + + + + True + False + Go + + + False + True + 1 + + + + + + + False + True + 2 + + + + + False + True + 1 + + + + + True + True + in + + + True + True + + + + + + + + + True + True + 2 + + + + + + diff --git a/Dynamic_User_Interfaces/LoadFileBrowser.py b/Dynamic_User_Interfaces/LoadFileBrowser.py new file mode 100644 index 0000000..d04d8ae --- /dev/null +++ b/Dynamic_User_Interfaces/LoadFileBrowser.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +class SignalHandlers(): + + def on_back_clicked(self, button ): + pass + + def on_forward_clicked(self, button ): + pass + + def on_up_clicked(self, button ): + pass + + def on_refresh_clicked(self, button ): + pass + + def on_home_clicked(self, button ): + pass + + def on_delete_clicked(self, button ): + pass + + def on_info_clicked(self, button ): + pass + + def on_go_clicked(self, button ): + pass + + def on_location_activate(self, button ): + pass + + def on_row_activated(self, button ): + pass + + def on_window_destroy(self, button ): + pass + +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: + builder = Gtk.Builder() + builder.add_from_file("./FileBrowser.glade") + self.window = builder.get_object("main_window") + self.add_window(self.window) + builder.connect_signals(SignalHandlers()) + self.add_window(self.window) + self.window.show_all() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Integrating/Calculator.glade b/Integrating/Calculator.glade new file mode 100644 index 0000000..a512bdb --- /dev/null +++ b/Integrating/Calculator.glade @@ -0,0 +1,317 @@ + + + + + + Calculator + False + False + + + + + + True + False + vertical + 7 + + + True + True + 2 + 2 + 3 + 3 + False + False + 1 + number + + + False + True + 0 + + + + + True + False + 3 + 3 + 4 + 7 + 7 + True + + + Xy + True + True + True + + + + 4 + 3 + + + + + Sqrt + True + True + True + + + + 4 + 2 + + + + + +/- + True + True + True + + + + 4 + 1 + + + + + + + True + True + True + + + + 3 + 3 + + + + + - + True + True + True + + + + 3 + 2 + + + + + X + True + True + True + + + + 3 + 1 + + + + + = + True + True + True + + + + 2 + 3 + + + + + 3 + True + True + True + + + + 2 + 2 + + + + + 6 + True + True + True + + + + 2 + 1 + + + + + Clr + True + True + True + + + + 4 + 0 + + + + + / + True + True + True + + + + 3 + 0 + + + + + 9 + True + True + True + + + + 2 + 0 + + + + + . + True + True + True + + + + 1 + 3 + + + + + 2 + True + True + True + + + + 1 + 2 + + + + + 5 + True + True + True + + + + 1 + 1 + + + + + 8 + True + True + True + + + + 1 + 0 + + + + + 0 + True + True + True + + + + 0 + 3 + + + + + 1 + True + True + True + + + + 0 + 2 + + + + + 4 + True + True + True + + + + 0 + 1 + + + + + 7 + True + True + True + + + + 0 + 0 + + + + + False + True + 1 + + + + + + diff --git a/Integrating/Calculator.py b/Integrating/Calculator.py new file mode 100644 index 0000000..bb7e435 --- /dev/null +++ b/Integrating/Calculator.py @@ -0,0 +1,178 @@ +#!/usr/bin/python3 + +import sys +import math +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Pango + + + +class SignalHandlers(): + + def __init__(self, builder): + self.builder = builder + self.entry = None + self.OP_NULL = 0 + self.OP_ADD = 1 + self.OP_SUBTRACT = 2 + self.OP_MULTIPLY = 3 + self.OP_DIVIDE = 4 + self.OP_POWER = 5 + self.clear_value = False + self.value_set = False + self.prev_value = 0 + self.pending_op = self.OP_NULL + + entry = builder.get_object("output") + self.entry = entry + fd = Pango.font_description_from_string("Monospace Bold 16") + entry.set_text("0") + entry.modify_font(fd) + + # Set user data for the operators and decimal buttons. + add = builder.get_object("add") + add.operator = self.OP_ADD + sub = builder.get_object("sub") + sub.operator = self.OP_SUBTRACT + mul = builder.get_object("mul") + mul.operator = self.OP_MULTIPLY + div = builder.get_object("div") + div.operator = self.OP_DIVIDE + power = builder.get_object("power") + power.operator = self.OP_POWER + decimal = builder.get_object("decimal") + decimal.number = 10 + + # Set the user data for the number buttons. + for i in range(0, 10): + name = "num_%i" % (i,) + num = builder.get_object(name) + num.number = i + + def do_operation(self, entry, value): + # Perform the specified operation, either add, subtract, multiply, + # divide, or the power operation. + + # Perform the operation on prev_value with the new value and store + # the result back into prev_value. + if self.pending_op == self.OP_ADD: + self.prev_value += value + elif self.pending_op == self.OP_SUBTRACT: + self.prev_value -= value + elif self.pending_op == self.OP_MULTIPLY: + self.prev_value *= value + elif self.pending_op == self.OP_DIVIDE: + self.prev_value /= value; + elif self.pending_op == self.OP_POWER: + self.prev_value = pow(prev_value, value) + else: + return + + # Reset the pending operation and create a string with the new value. + self.pending_op = self.OP_NULL + entry.set_text(self.format_num(self.prev_value)) + + def on_num_clicked(self, button): + # Retrieve the number that is stored in user data. + num = button.number + + # Clear the value if a new number should be entered. + if self.clear_value: + self.entry.set_text("0") + self.clear_value = False + + # Append a decimal place to the GtkEntry. Make sure to keep track of + # whether the decimal place was already entered. + text = self.entry.get_text() + if (num == 10): + if len(text) > 9: + return + elif text.find('.') >= 0: + return + else: + text = text + '.' + # Append a number place to the GtkEntry if the length is less than 10. + else: + text = text + str(num) + if len(text) > 10: + return + # Remove preceeding zeros. + text = text.lstrip('0') + self.entry.set_text(text) + + def on_equal_clicked(self, button): + # Perform any pending operations because the equal button was pressed. + value = float(self.entry.get_text()) + + self.do_operation(self.entry, value) + self.clear_value = True + self.value_set = False + + def on_op_clicked(self, button): + op = button.operator + value = float(self.entry.get_text()) + + # Perform any pending operations and then store the new operation. + self.do_operation(self.entry, value) + self.pending_op = op; + self.clear_value = True + + # Set the current value as the previous value if it should be + # overwritten. + if not self.value_set: + self.prev_value = value; + self.value_set = True + + def on_sign_clicked(self, button): + value = float(self.entry.get_text()) + + # You cannot negate a value of zero. + if value == 0.0: + return + value *= -1 + + self.entry.set_text(self.format_num(value)) + + def on_sqrt_clicked(self, button): + # Take the square root of the current value. + value = math.sqrt(float(self.entry.get_text())) + + self.entry.set_text(self.format_num(value)) + + def on_clear_clicked(self, button): + self.entry.set_text("0") + + self.clear_value = False + self.value_set = False + self.prev_value = 0 + self.pending_op = self.OP_NULL + + def format_num(self, num): + text = str(num) + text = text.rstrip('0') + text = text.lstrip('0') + text = text.rstrip('.') # remove trailing decimal + return text + +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(*args, application_id="org.example.myapp", + **kwargs) + self.window = None + self.builder = None + + def do_activate(self): + if not self.window: + self.builder = Gtk.Builder() + self.builder.add_from_file("./Calculator.glade") + self.window = self.builder.get_object("window") + self.builder.connect_signals(SignalHandlers(self.builder)) + self.add_window(self.window) + self.window.show_all() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) + diff --git a/Integrating/Calendar.glade b/Integrating/Calendar.glade new file mode 100644 index 0000000..26210e5 --- /dev/null +++ b/Integrating/Calendar.glade @@ -0,0 +1,432 @@ + + + + + + True + False + window-close + + + True + False + list-add + + + + + + + + + + + + + -1 + False + + + + + + True + False + 3 + 3 + vertical + + + True + False + both + 2 + + + True + False + New + True + document-new + + + + False + False + + + + + True + False + Open + True + document-open + + + + False + False + + + + + True + False + + + False + True + + + + + True + False + Add Event + True + list-add + + + + False + False + + + + + True + False + Remove + True + list-remove + + + + False + False + + + + + True + False + Clear All + True + edit-cut + + + + False + False + + + + + False + True + 0 + + + + + True + True + + + True + False + vertical + + + True + False + Create or open a calendar ... + + + + + + False + True + 0 + + + + + True + True + 2018 + 5 + 28 + + + + + False + True + 1 + + + + + False + False + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + 1 + 1 + 1 + 1 + liststore + + + + + + + + True + False + + + + + True + True + 1 + + + + + + + False + Add a New Event + dialog + window + + + + + + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + vertical + 2 + + + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + end + + + Cancel + True + True + True + image1 + True + + + True + True + 0 + + + + + Add Event + True + True + True + image2 + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + 3 + 3 + 7 + 10 + + + True + False + Name: + 1 + + + 0 + 0 + + + + + True + False + Location: + 1 + + + 0 + 1 + + + + + True + False + Start Time: + 1 + + + 0 + 2 + + + + + True + False + End Time: + 1 + + + 0 + 3 + + + + + True + True + + + 1 + 0 + + + + + True + True + + + 1 + 1 + + + + + True + False + + All Day + 00:00 + 01:00 + 02:00 + 03:00 + 04:00 + 05:00 + 06:00 + 07:00 + 08:00 + 09:00 + 10:00 + 11:00 + 12:00 + 13:00 + 14:00 + 15:00 + 16:00 + 17:00 + 18:00 + 19:00 + 20:00 + 21:00 + 22:00 + 23:00 + + + + 1 + 2 + + + + + True + False + + 02:00 + 03:00 + 04:00 + 05:00 + 06:00 + 07:00 + 08:00 + 09:00 + 10:00 + 11:00 + 12:00 + 13:00 + 14:00 + 15:00 + 16:00 + 17:00 + 18:00 + 19:00 + 20:00 + 21:00 + 22:00 + 23:00 + 24:00 + + + + 1 + 3 + + + + + False + True + 1 + + + + + + button1 + button2 + + + diff --git a/Integrating/Calendar.py b/Integrating/Calendar.py new file mode 100644 index 0000000..981fc57 --- /dev/null +++ b/Integrating/Calendar.py @@ -0,0 +1,277 @@ +#!/usr/bin/python3 + +import sys +import xml.sax +import time +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + + +class Event(): + # A structure used to hold a single event. + def __init__(self): + self.name = None + self.location = None + self.day = None + self.month = None + self.year = None + self.start = None + self.end = None + + +class CalendarContentHandler(xml.sax.ContentHandler): + + def __init__(self): + super().__init__() + self.event = None # the current event + self.tag_name = None # the current xml tag name + self.events = [] # the list of events + + def startElement(self, name, attrs): + if name == "event": + self.event = Event() + self.tag_name = name + + def endElement(self, name): + if name == "event": + self.events.append(self.event) + + def characters(self, content): + cdata = content.strip() + if cdata == None or cdata == "": + return + if self.tag_name == "name": + self.event.name = cdata + elif self.tag_name == "location": + self.event.location = cdata + elif self.tag_name == "day": + self.event.day = cdata + elif self.tag_name == "month": + self.event.month = cdata + elif self.tag_name == "year": + self.event.year = cdata + elif self.tag_name == "start": + self.event.start = cdata + elif self.tag_name == "end": + self.event.end = cdata + else: + return + + +class SignalHandlers(): + + def __init__(self, builder): + self.builder = builder + self.events = [] # the list of events + self.NAME = 0 # treeview column 1 + self.LOCATION = 1 # treeview column 2 + self.TIME = 2 # treeview column 3 + self.filename = None # the current events file + + + def on_new_clicked(self, button): + window = self.builder.get_object("window") + dialog = Gtk.FileChooserDialog(title="Create a New Calendar", + action=Gtk.FileChooserAction.SAVE, + modal=True, parent=window, + buttons=("Cancel", + Gtk.ResponseType.CANCEL, + "Save", + Gtk.ResponseType.ACCEPT)) + dialog.set_do_overwrite_confirmation(False) + result = dialog.run() + if result == Gtk.ResponseType.ACCEPT: + self.save_calendar_list() + self.filename = dialog.get_filename() + self.events = [] + self. load_calendar_list(self.filename) + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + dialog.destroy() + + + def on_open_clicked(self, button): + window = self.builder.get_object("window") + dialog = Gtk.FileChooserDialog(title="Open a New Calendar", + action=Gtk.FileChooserAction.OPEN, + modal=True, parent=window, + buttons=("Cancel", + Gtk.ResponseType.CANCEL, + "Open", + Gtk.ResponseType.ACCEPT)) + dialog.set_do_overwrite_confirmation(False) + result = dialog.run() + if result == Gtk.ResponseType.ACCEPT: + self.save_calendar_list() + self.filename = dialog.get_filename() + self.events = [] + self. load_calendar_list(self.filename) + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + dialog.destroy() + + + def on_add_clicked(self, button): + dialog = self.builder.get_object("dialog") + name = self.builder.get_object("event_name") + location = self.builder.get_object("location") + start = self.builder.get_object("start_time") + end = self.builder.get_object("end_time") + # Reset the event name and location since the dialog is reused. + name.set_text("") + location.set_text("") + response = dialog.run() + if response == Gtk.ResponseType.APPLY: + # Create a new event with the entered name, location and start time. + calendar = self.builder.get_object("calendar") + (year, month, day) = calendar.get_date() + calendar = self.builder.get_object("calendar") + (year, month, day) = calendar.get_date() + event = Event() + event.day = str(day) + event.month = str(month+1) + event.year = str(year) + event.name = name.get_text() + event.location = location.get_text() + event.start = start.get_active_text() + + # If the start time is "All Day", make the end time an empty string. + if event.start == "All Day": + event.end = "" + else: + event.end = end.get_active_text() + self.events.append(event) + self.save_calendar_list() + self.on_day_changed(calendar) + dialog.hide() + + + def on_remove_clicked(self, button): + treeview = self.builder.get_object("treeview") + store = self.builder.get_object("liststore") + selection = treeview.get_selection() + (model, iter) = selection.get_selected() + if iter == None: + return + (name, location, time) = store.get(iter, self.NAME, self.LOCATION, self.TIME) + words = time.split() + if len(words) == 2: + # start time was "All Day" + start = time + end = "" + else: + start = words[0] + end = words[2] + for event in self.events: + if name == event.name and location == event.location and start == event.start and end == event.end: + idx = self.events.index(event) + del self.events[idx] + self.save_calendar_list() + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + + + def on_clear_clicked(self, button): + self.events = [] + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + + + def on_month_changed(self, calendar): + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + + + def on_day_changed(self, calendar): + treeview = self.builder.get_object("treeview") + store = self.builder.get_object("liststore") + store.clear() + (year, month, day) = calendar.get_date() + for event in self.events: + if event.end == None: + event.end = "" + if event.year == str(year) and event.month == str(month+1) and \ + event.day == str(day): + if event.start != "All Day": + time = event.start + " to " + event.end + else: + time = event.start + iter = store.append() + store.set(iter, (self.NAME, self.LOCATION, self.TIME), + (event.name, event.location, time)) + + + def load_calendar_list(self, filename): + contenthandler = CalendarContentHandler() + xml.sax.parse(filename, contenthandler) + self.filename = filename + self.events = contenthandler.events + calendar = self.builder.get_object("calendar") + self.on_day_changed(calendar) + + + def save_calendar_list(self): + f = open(self.filename, 'w') + f.write("\n") + for event in self.events: + f.write(" \n") + f.write(" " + event.name + "\n") + f.write(" " + event.location + "\n") + f.write(" " + event.day + "\n") + f.write(" " + event.month + "\n") + f.write(" " + event.year + "\n") + f.write(" " + event.start + "\n") + if event.end == None: + event.end = "" + f.write(" " + event.end + "\n") + f.write(" \n") + f.write("\n") + f.close() + + +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(application_id="org.example.calendar", + *args, **kwargs) + self.window = None + + + def do_activate(self): + if not self.window: + builder = Gtk.Builder() + builder.add_from_file("./Calendar.glade") + self.window = builder.get_object("window") + self.add_window(self.window) + self.NAME = 0 + self.LOCATION = 1 + self.TIME = 2 + self.treeview = builder.get_object("treeview") + self.config_treeview_columns(builder) + sig = SignalHandlers(builder) + builder.connect_signals(sig) + sig.load_calendar_list("./Calendar_File.xml") + self.window.show_all() + + + def config_treeview_columns(self, builder): + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn(cell_renderer=renderer, title="Event Name", text=self.NAME) + self.treeview.append_column(column) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn(cell_renderer=renderer, title="Location", text=self.LOCATION) + self.treeview.append_column(column) + + renderer = Gtk.CellRendererText() + column = Gtk.TreeViewColumn(cell_renderer=renderer, title="Event Time", text=self.TIME) + self.treeview.append_column(column) + + store = builder.get_object("liststore") + self.treeview.set_model(store) + + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Integrating/Calendar_File.xml b/Integrating/Calendar_File.xml new file mode 100644 index 0000000..742bc83 --- /dev/null +++ b/Integrating/Calendar_File.xml @@ -0,0 +1,11 @@ + + + Independence Day + Everywhere + 4 + 7 + 2018 + All Day + + + diff --git a/Integrating/FileBrowser.glade b/Integrating/FileBrowser.glade new file mode 100644 index 0000000..b2584ee --- /dev/null +++ b/Integrating/FileBrowser.glade @@ -0,0 +1,507 @@ + + + + + + False + dialog + + + + + + dialog + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + vertical + 2 + + + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK + end + + + gtk-close + True + True + True + True + True + + + True + True + 0 + + + + + False + False + 0 + + + + + grid1 + True + False + 5 + 10 + + + True + False + Name: + 1 + + + + + + 0 + 0 + + + + + True + False + File Name + 0 + + + 1 + 0 + + + + + True + False + + + 1 + 5 + + + + + True + False + + + 0 + 5 + + + + + True + False + Type: + 1 + + + + + + 0 + 1 + + + + + True + False + Location: + 1 + + + + + + 0 + 2 + + + + + True + False + File/Directory + 0 + 1 + + + 1 + 2 + + + + + label3 + True + False + Size: + 1 + + + + + + 0 + 3 + + + + + True + False + 5 KB + 0 + + + 1 + 3 + + + + + True + False + Last Modified: + 1 + 0.5 + + + + + + 0 + 4 + + + + + True + False + Day Mth DD HH:MM:SS YYYY + 0 + + + 1 + 4 + + + + + True + False + Last Accessed: + 1 + + + + + + 0 + 6 + + + + + True + False + Day Mth DD HH:MM:SS YYYY + 0 + + + 1 + 6 + + + + + True + False + File Type + 0 + + + 1 + 1 + + + + + False + False + 5 + 1 + + + + + + button1 + + + + 500 + False + File Browser + + + + + + True + False + vertical + + + True + False + both + False + 2 + + + True + False + Back + True + gtk-go-back + + + False + False + + + + + True + False + Forward + True + gtk-go-forward + + + False + False + + + + + True + False + Up + True + gtk-go-up + + + False + False + + + + + True + False + + + False + True + + + + + True + False + Refresh + True + gtk-refresh + + + False + False + + + + + True + False + Home + True + gtk-home + + + False + False + + + + + True + False + + + False + True + + + + + True + False + Delete + True + gtk-delete + + + False + False + + + + + True + False + Information + True + gtk-dialog-info + + + + False + False + + + + + False + True + 0 + + + + + True + False + 5 + + + True + False + 5 + 5 + Current Location: + True + + + False + True + 0 + + + + + True + True + + + True + True + 1 + + + + + True + True + True + none + + + + True + False + 3 + + + True + False + gtk-jump-to + + + False + True + 0 + + + + + True + False + Go + + + False + True + 1 + + + + + + + False + True + 2 + + + + + False + True + 1 + + + + + True + True + in + + + True + True + + + + + + + + True + True + 2 + + + + + True + True + + + False + False + 3 + + + + + + diff --git a/Integrating/FileBrowser.py b/Integrating/FileBrowser.py new file mode 100644 index 0000000..ab4cd93 --- /dev/null +++ b/Integrating/FileBrowser.py @@ -0,0 +1,385 @@ +#!/usr/bin/python3 + +import sys +import os, time +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GdkPixbuf, GObject + +ICON = 0 +FILENAME = 1 +SIZE = 2 +MODIFIED = 3 + +size_type = ["B", "KiB", "MiB", "GiB"] + +class History(): + + def __init__(self): + self.stack = [] + self.pos = -1 + +class Application(Gtk.Application): + + def __init__(self, *args, **kwargs): + super().__init__(application_id="org.example.myapp", *args, + **kwargs) + self.window = None + self.dialog = None + self.builder = None + self.treeview = None + self.history = History() + self.statusbar = None + + def do_activate(self): + if not self.window: + self.builder = Gtk.Builder() + self.builder.add_from_file("./FileBrowser.glade") + self.window = self.builder.get_object("window") + self.treeview = self.builder.get_object("treeview") + + # Glade does not allow us to add non-GTK items to the user data + # for connection. Also, the Gtk.Builder widget is absent from + # Glade as well. Therefore we have to make our connections to + # the signal handlers manually. + info = self.builder.get_object("info") + info.connect_after("clicked", self.on_info_clicked, self.builder, + self.history) + + self.treeview.connect_after("row_activated", self.on_row_activated, + self.builder, self.history) + + back = self.builder.get_object("back") + back.connect_after("clicked", self.on_back_clicked, self.builder, + self.history) + + forward = self.builder.get_object("forward") + forward.connect_after("clicked", self.on_forward_clicked, self.builder, + self.history) + + up = self.builder.get_object("up") + up.connect_after("clicked", self.on_up_clicked, self.builder, + self.history) + + home = self.builder.get_object("home") + home.connect_after("clicked", self.on_home_clicked, self.builder, + self.history) + + delete = self.builder.get_object("delete") + delete.connect_after("clicked", self.on_delete_clicked, self.builder) + + go = self.builder.get_object("go") + go.connect_after("clicked", self.on_go_clicked, self.builder, + self.history) + + entry = self.builder.get_object("location") + entry.connect_after("activate", self.on_location_activate, self.builder, + self.history) + + refresh = self.builder.get_object("refresh") + refresh.connect_after("clicked", self.on_refresh_clicked, self.builder) + + self.setup_tree_view(self.treeview) + self.setup_tree_model(self.treeview) + self.add_window(self.window) + self.window.show_all() + + def setup_tree_view(self, treeview): + # Create a tree view column with an icon and file name. + column = Gtk.TreeViewColumn.new() + column.set_title("File Name") + + renderer = Gtk.CellRendererPixbuf.new() + column.pack_start(renderer, False) + column.set_attributes(renderer, pixbuf=ICON) + + renderer = Gtk.CellRendererText.new() + column.pack_start(renderer, True) + column.set_attributes(renderer, text=FILENAME) + + treeview.append_column(column) + + # Insert a second tree view column that displays the file size. + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn.new() + column.set_title("Size") + column.pack_start(renderer, True) + column.set_attributes(renderer, text=SIZE) + treeview.append_column(column) + + # Insert a third tree view column that displays the last modified time/date. + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn.new() + column.set_title("Last Modified") + column.pack_start(renderer, True) + column.set_attributes(renderer, text=MODIFIED) + treeview.append_column(column) + + def setup_tree_model(self, treeview): + store = Gtk.ListStore.new((GdkPixbuf.Pixbuf, GObject.TYPE_STRING, + GObject.TYPE_STRING, GObject.TYPE_STRING)) + treeview.set_model(store) + self.populate_tree_model(self.builder) + + def populate_tree_model(self, builder): + treeview = builder.get_object("treeview") + total_size = 0 + items = 0 + store = treeview.get_model() + store.clear() + location = os.getcwd() + + # If the current location is not the root directory, add the '..' entry. + if len(location) > 1: + icon_theme = Gtk.IconTheme.get_default() + icon = icon_theme.load_icon("folder", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + iter = store.append() + store.set(iter, (ICON, FILENAME), (icon, "..")) + + # Return if the path does not exist. + if not os.path.isdir(location): + self.file_manager_error("The path %s does not exist!" % (location,), builder) + + # Display the new location in the address bar. + entry = builder.get_object("location") + entry.set_text(location) + + # Add each file to the list along with the file size and modified date. + pixbuf_dir = icon + pixbuf_file = icon_theme.load_icon("text-x-generic", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + files = os.listdir(location) + for file in files: + fn = location + "/" + file + st = os.stat(fn) + if st: + # Calculate the file size and order of magnitude. + i = 0 + size = st.st_size + total_size = size; + while size >= 1024.0: + size = size / 1024.0 + i += 1 + + # Create strings for the file size and last modified date. + filesize = "%.1f %s" % (size, size_type[i]) + modified = time.ctime(st.st_mtime) + + # Add the file and its properties as a new tree view row. + iter = store.append() + + if os.path.isdir(fn): + store.set(iter, (ICON, FILENAME, SIZE, MODIFIED), + (pixbuf_dir, file, filesize, modified)) + else: + store.set(iter, (ICON, FILENAME, SIZE, MODIFIED), + (pixbuf_file, file, filesize, modified)) + items += 1 + + # Calculate the total size of the directory content. + i = 0 + while total_size >= 1024.0: + total_size = total_size / 1024.0 + i += 1 + + # Add the number of items and the total size of the directory content + # to the status bar. + statusbar = builder.get_object("statusbar") + message = "%d items, Total Size: %.1f %s" % (items, + total_size, size_type[i]) + context_id = statusbar.get_context_id("FileBrowser") + statusbar.pop(context_id) + statusbar.push(context_id, message) + + def file_manager_error(self, message, builder): + window = builder.get_object("window") + dialog = builder.get_object("dialog") + dialog = Gtk.MessageDialog(parent=window, modal=True, + message_type=Gtk.MessageType.ERROR, + buttons=("OK", Gtk.ResponseType.OK), + title="File Manager Error") + dialog.format_secondary_text(message) + dialog.run() + dialog.destroy() + + def on_info_clicked(self, button, builder, history): + treeview = builder.get_object("treeview") + dialog = builder.get_object("dialog") + selection = treeview.get_selection() + + (model, iter) = selection.get_selected() + if iter: + name = builder.get_object("name") + loc = builder.get_object("location") + type = builder.get_object("type") + size = builder.get_object("size") + mod = builder.get_object("modified") + accessed = builder.get_object("accessed") + (file,) = model.get(iter, FILENAME) + + # Set the file name, location and whether it is a file or directory. + location = os.getcwd() + name.set_text(file) + loc.set_text(location) + file = location + "/" + file + if os.path.isdir(file): + type.set_text("Directory") + else: + type.set_text("File") + + # Set the file size, last modified date and last accessed date. + st = os.stat(file) + if st: + i = 0 + file_size = st.st_size + while file_size >= 1024.0: + file_size = file_size / 1024.0 + i += 1 + + modified = time.ctime(st.st_mtime) + access = time.ctime(st.st_atime) + mod.set_text(modified) + accessed.set_text(access) + size.set_text("%.1f %s" % (file_size, size_type[i])) + + dialog.run() + dialog.hide() + + def on_back_clicked(self, button, builder, history): + if len(history.stack) > 0: + if history.pos > -1: + previous_loc = history.stack[history.pos] + history.pos -= 1 + os.chdir(previous_loc) + self.populate_tree_model(builder) + + def on_forward_clicked(self, button, builder, history ): + if history.pos < len(history.stack) - 1: + history.pos += 1 + next_loc = history.stack[history.pos] + os.chdir(next_loc) + self.populate_tree_model(builder) + + def on_up_clicked(self, button, builder, history): + os.chdir("..") + location = os.getcwd() + + entry = builder.get_object("location") + entry.set_text(location) + go = builder.get_object("go") + self.on_go_clicked(go, builder, history) + + def on_refresh_clicked(self, button, builder): + self.populate_tree_model(builder) + + def on_home_clicked(self, button, builder, history): + go = builder.get_object("go") + entry = builder.get_object("location") + entry.set_text(os.getenv("HOME")) + self.on_go_clicked(go, builder, history) + + def on_delete_clicked(self, button, builder): + entry = builder.get_object("location") + treeview = builder.get_object("treeview") + window = builder.get_object("window") + location = os.getcwd() + + # If there is a selected file, delete it if the user approves. + selection = treeview.get_selection() + (model, iter) = selection.get_selected() + if iter: + (file,) = model.get(iter, FILENAME) + if file == "..": + self.file_manager_error("You cannot remove the '..' directory!", + builder) + return + + entry.set_text(location) + rmlocation = location + "/" + file + + # Ask the user if it is okay to remove the file or folder. + msg = "Do you really want to delete %s?" % file + dialog = Gtk.MessageDialog(parent=window, modal=True, + message_type=Gtk.MessageType.QUESTION, + buttons=("Yes", Gtk.ResponseType.YES, + "No", Gtk.ResponseType.NO), + text=msg) + + # If the user approves, remove the file or report ane error. + response = dialog.run() + if response == Gtk.ResponseType.YES: + if os.path.isdir(rmlocation): + try: + shutil.rmtree(rmlocation) + except Exception as e: + self.file_manager_error( + "The file or folder could not be removed!", builder) + self.populate_tree_model(builder) + else: + try: + os.remove(rmlocation) + except Exception as e: + self.file_manager_error( + "The file or folder could not be removed!", builder) + self.populate_tree_model(builder) + dialog.destroy() + + def on_go_clicked(self, button, builder, history): + treeview = builder.get_object("treeview") + entry = builder.get_object("location") + location = entry.get_text() + + # If the directory exists, visit the entered location. + if os.path.isdir(location): + self.store_history(history) + os.chdir(location) + self.populate_tree_model(builder) + else: + self.file_manager_error("The location does not exist!", builder) + + def on_location_activate(self, button, builder, history): + go = builder.get_object("go") + self.on_go_clicked(go, builder, history) + + def on_row_activated(self, treeview, path, column, builder, history): + treeview = builder.get_object("treeview") + model = treeview.get_model() + iter = model.get_iter(path) + if iter: + (file,) = model.get(iter, FILENAME) + + # Move to the parent directory. + if file == "..": + self.store_history(history) + os.chdir("..") + self.populate_tree_model(builder) + # Move to the chosen directory or show more information about the file. + else: + location = os.getcwd() + "/" + file + if os.path.isdir(location): + self.store_history(history) + os.chdir(location) + self.populate_tree_model(builder) + else: + info = builder.get_object("info") + self.on_info_clicked(info, builder, history) + + def store_history(self, history): + location = os.getcwd() + history.stack.append(location) + history.pos = len(history.stack) - 1 + + def pop_history(self, history): + if len(history.stack) > 0: + history.stack.pop() + if history.stack.pos > len(history.stack) - 1: + history.pos = len(history.stack) - 1 + else: + history.pos -= 1 + + + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Integrating/Ping.glade b/Integrating/Ping.glade new file mode 100644 index 0000000..1e74ecb --- /dev/null +++ b/Integrating/Ping.glade @@ -0,0 +1,234 @@ + + + + + + 100 + 1 + 10 + 10 + + + 100 + 1 + 10 + + + True + False + network-transmit + + + True + False + process-stop + + + True + False + window-close + + + False + 7 + 450 + 400 + False + + + + + + True + False + vertical + 10 + + + True + False + 7 + 7 + + + True + False + Address: + 1 + + + 0 + 0 + + + + + True + False + Pings: + 1 + + + 0 + 1 + + + + + True + True + + + 1 + 0 + 4 + + + + + Continuous requests + True + True + False + True + True + requests_num + + + 4 + 1 + + + + + True + False + requests + + + 3 + 1 + + + + + True + True + adjustment1 + + + 2 + 1 + + + + + True + True + False + True + True + requests_all + + + 1 + 1 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + True + True + + + + + + + + False + True + 1 + + + + + True + False + 5 + True + end + + + Ping + True + True + True + image1 + True + + + + True + True + 0 + + + + + Stop + True + False + True + True + image2 + True + + + + True + True + 1 + + + + + Close + True + True + True + image3 + True + + + + True + True + 2 + + + + + False + True + 2 + + + + + + diff --git a/Integrating/Ping.py b/Integrating/Ping.py new file mode 100644 index 0000000..8e07a27 --- /dev/null +++ b/Integrating/Ping.py @@ -0,0 +1,201 @@ +#!/usr/bin/python3 + +import sys +import os, signal +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GLib, GObject + +class SignalHandlers(): + + def __init__(self, builder): + self.builder = builder + self.SEQ = 0 + self.ADDRESS = 1 + self.TTL = 2 + self.TIME = 3 + self.UNITS = 4 + self.channel = None + self.pid = None + self.window = builder.get_object("window") + self.treeview = builder.get_object("output") + self.cpid = None + self.setup_tree_view(self.treeview) + + def on_ping_clicked(self, button): + requests = self.builder.get_object("requests") + radio = self.builder.get_object("requests_num") + address = self.builder.get_object("address") + treeview = self.treeview + + # Create a new tree model with five columns for ping output. + store = Gtk.ListStore.new((str, str, str, str, str)) + self.treeview.set_model(store) + + # Retrieve the current ping parameters entered by the user. + num = requests.get_value_as_int() + location = address.get_text() + + # Return if an address was not entered into the GtkEntry widget. + if len(location) == 0: + return + # Otherwise, build the command based upon the user's preferences. + elif radio.get_active(): + if num == 0: + return + command = "ping " + location + " -c " + str(num) + else: + command = "ping " + location + + # Parse the command and launch the process, monitoring standard output. + (bool, argvp) = GLib.shell_parse_argv(command) + if bool: + (ret, self.cpid, fin, fout, ferr) = GLib.spawn_async_with_pipes(None, + argvp, None, GLib.SpawnFlags.SEARCH_PATH, + None, None) + if not ret: + print("The 'ping' instruction has failed!") + else: + # Disable the Ping button and enable the Stop button. + stop = self.builder.get_object("stop") + stop.set_sensitive(True) + ping = self.builder.get_object("ping") + ping.set_sensitive(False) + + # Create a new IO channel and monitor it for data to read. + channel = GLib.IOChannel.unix_new(fout) + channel.add_watch(GLib.IOCondition.IN | GLib.IOCondition.ERR | + GLib.IOCondition.HUP, self.read_output, None) + + def on_stop_clicked(self, button): + stop = self.builder.get_object("stop") + stop.set_sensitive(False) + ping = self.builder.get_object("ping") + ping.set_sensitive(True) + + os.kill(self.cpid, signal.SIGINT) + self.cpid = None + + def on_window_close(self, button): + self.window.destroy() + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn(title="Seq", cell_renderer=renderer, + text=self.SEQ) + treeview.append_column(column) + + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn(title="Address", cell_renderer=renderer, + text=self.ADDRESS) + treeview.append_column(column) + + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn(title="TTL", cell_renderer=renderer, + text=self.TTL) + treeview.append_column(column) + + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn(title="Time", cell_renderer=renderer, + text=self.TIME) + treeview.append_column(column) + + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn(title="Units", cell_renderer=renderer, + text=self.UNITS) + treeview.append_column(column) + + + def read_output(self, channel, condition, data): + # Read the current line of data from the IO channel. + errstatus = False + try: + (status, line, length, term) = channel.read_line() + except IOError: + print("Error reading IO channel.") + errstatus = True + + # If some type of error has occurred, handle it. + if errstatus or status != GLib.IOStatus.NORMAL or line == None: + print("Error reading IO channel.") + + # Disable the stop button and enable the ping button for future action. + stop = self.builder.get_object("stop") + stop.set_sensitive(False) + ping = self.builder.get_object("ping") + ping.set_sensitive(True) + + if channel != None: + channel.shutdown(True) + channel = None + + return False + + # Parse the line if an error has not occurred. + self.parse_output(line) + + return True + + def parse_output(self, buffer): + # Load the list store, split the string into lines and create a pointer. + store = self.treeview.get_model() + lines = buffer.splitlines() + + # Loop through each of the lines, parsing its content. + for line in lines: + # If this is an empty string, move to the next string. + if line == "": + continue + # Parse the line if it contains information about the current ping. + elif line.find("64 bytes") >= 0: + # Extract the IP address of the computer you are pinging. + result = line.split("from ") + result = result[1].split(':') + address = result[0] + + # Extract the current ping count from the output. + result = line.split("seq=") + result = result[1].split(" ") + seq = result[0] + + # Extract the IP's "Time To Live" from the output. + result = line.split("ttl=") + result = result[1].split(" ") + ttl = result[0] + + # Extract the time it took for this ping operation from the output. + result = line.split("time=") + result = result[1].split(" ") + time = result[0] + + # Extrace the time units from the output. + result = result[1].split(" ") + units = result[0] + + # Append the information for the current ping operation. + iter = store.append (); + store.set (iter, (self.SEQ, self.ADDRESS, self.TTL, self.TIME, + self.UNITS), + (seq, address, ttl, time, units)) + + +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: + builder = Gtk.Builder() + builder.add_from_file("./Ping.glade") + self.window = builder.get_object("window") + builder.connect_signals(SignalHandlers(builder)) + self.add_window(self.window) + self.window.show_all() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..68e31c3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Freeware License, some rights reserved + +Copyright (c) 2019 W. David Ashley and Andrew Krause + +Permission is hereby granted, free of charge, to anyone obtaining a copy +of this software and associated documentation files (the "Software"), +to work with the Software within the limits of freeware distribution and fair use. +This includes the rights to use, copy, and modify the Software for personal use. +Users are also allowed and encouraged to submit corrections and modifications +to the Software for the benefit of other users. + +It is not allowed to reuse, modify, or redistribute the Software for +commercial use in any way, or for a user’s educational materials such as books +or blog articles without prior permission from the copyright holder. + +The above copyright notice and this permission notice need to be included +in all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS OR APRESS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/Menus_And_Toolbars/Exercise_1.py b/Menus_And_Toolbars/Exercise_1.py new file mode 100644 index 0000000..bb4f162 --- /dev/null +++ b/Menus_And_Toolbars/Exercise_1.py @@ -0,0 +1,100 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + progress.set_show_text(True) + self.create_popup_menu(menu, progress) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + self.hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + self.hbox.pack_start(eventbox, False, False, 5) + self.add(self.hbox) + + def create_popup_menu(self, menu, progress): + pulse = Gtk.MenuItem.new_with_label("Pulse Progress") + fill = Gtk.MenuItem.new_with_label("Set as Complete") + clear = Gtk.MenuItem.new_with_label("Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + pass + + def pulse_activated(self, item, progress): + pass + + def fill_activated(self, item, progress): + pass + + def clear_activated(self, item, progress): + pass + + def on_cutstandard(self, widget): + pass + + def on_copystandard(self, widget): + pass + + def on_pastestandard(self, widget): + pass + + +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="Exercise 1") + builder = Gtk.Builder() + builder.add_from_file("./Exercise_1_Toolbar.xml") + builder.connect_signals(self.window) + toolbar = builder.get_object("toolbar") + toolbar.set_orientation(Gtk.Orientation.VERTICAL) + self.window.hbox.pack_end(toolbar, True, False, 1) + self.window.show_all() + self.window.present() + + def on_newstandard(self, widget): + pass + + def on_openstandard(self, widget): + pass + + def on_savestandard(self, widget): + pass + + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/Exercise_1_Toolbar.xml b/Menus_And_Toolbars/Exercise_1_Toolbar.xml new file mode 100644 index 0000000..1d02e25 --- /dev/null +++ b/Menus_And_Toolbars/Exercise_1_Toolbar.xml @@ -0,0 +1,92 @@ + + + + + True + False + + + True + False + New Standard + app.on_newstandard + document-new + + + False + True + + + + + True + False + Open Standard + app.on_openstandard + document-open + + + False + True + + + + + True + False + Save Standard + app.on_savestandard + document-save + + + False + True + + + + + True + False + + + + + True + False + Cut Standard + win.on_cutstandard + edit-cut + + + False + True + + + + + True + False + Copy Standard + win.on_copystandard + edit-copy + + + False + True + + + + + True + False + Paste Standard + win.on_pastestandard + edit-paste + + + False + True + + + + diff --git a/Menus_And_Toolbars/Exercise_2.py b/Menus_And_Toolbars/Exercise_2.py new file mode 100644 index 0000000..6627e5b --- /dev/null +++ b/Menus_And_Toolbars/Exercise_2.py @@ -0,0 +1,94 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + progress.set_show_text(True) + self.create_popup_menu(menu, progress) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + self.add(eventbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def create_popup_menu(self, menu, progress): + pulse = Gtk.MenuItem.new_with_label("Pulse Progress") + fill = Gtk.MenuItem.new_with_label("Set as Complete") + clear = Gtk.MenuItem.new_with_label("Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + pass + + def pulse_activated(self, item, progress): + pass + + def fill_activated(self, item, progress): + pass + + def clear_activated(self, item, progress): + pass + + def on_menu_cut(self, menuitem): + pass + + def on_menu_copy(self, menuitem): + pass + + def on_menu_paste(self, menuitem): + pass + +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="Exercise 2") + builder = Gtk.Builder() + builder.add_from_file("./Exercise_2_Menubar.xml") + self.set_menubar(builder.get_object("menubar")) + self.window.show_all() + self.window.present() + + def on_menu_new(self, menuitem): + pass + + def on_menu_open(self, menuitem): + pass + + def on_menu_save(self, menuitem): + pass + + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/Exercise_2_Menubar.xml b/Menus_And_Toolbars/Exercise_2_Menubar.xml new file mode 100644 index 0000000..eacaaba --- /dev/null +++ b/Menus_And_Toolbars/Exercise_2_Menubar.xml @@ -0,0 +1,91 @@ + + + + + + True + False + + + True + False + _File + True + + + True + False + + + gtk-new + True + False + True + + + + + + gtk-open + True + False + True + + + + + + gtk-save + True + False + True + + + + + + + + + + True + False + _Edit + True + + + True + False + + + gtk-cut + True + False + True + + + + + + gtk-copy + True + False + True + + + + + + gtk-paste + True + False + True + + + + + + + + + diff --git a/Menus_And_Toolbars/MenuAndToolbar.py b/Menus_And_Toolbars/MenuAndToolbar.py new file mode 100644 index 0000000..a76aa17 --- /dev/null +++ b/Menus_And_Toolbars/MenuAndToolbar.py @@ -0,0 +1,49 @@ +#!/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) + + def change_label(self): + pass + + def maximize(self): + pass + + def about(self): + pass + + def quit(self): + self.destroy() + + def newstandard(self): + pass + +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="Hello World!") + builder = Gtk.Builder() + builder.add_from_file("./Menu_XML_File.ui") + builder.add_from_file("./Toolbar_UI_File.xml") + builder.connect_signals(self.window) + self.set_menubar(builder.get_object("menubar")) + self.window.add(builder.get_object("toolbar")) + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/MenuToolButton.py b/Menus_And_Toolbars/MenuToolButton.py new file mode 100644 index 0000000..ca313be --- /dev/null +++ b/Menus_And_Toolbars/MenuToolButton.py @@ -0,0 +1,8 @@ +recent = Gtk.Menu.new() +# Add a number of menu items where each corresponds to one recent file. +icon_theme = Gtk.IconTheme.get_default() +icon = icon_theme.load_icon("document-open", -1, + Gtk.IconLookupFlags.FORCE_SIZE) +image = Gtk.Image.new_from_pixbuf(icon) +open = Gtk.MenuToolButton.new(image, label) +open.set_menu(recent) diff --git a/Menus_And_Toolbars/Menu_XML_File.ui b/Menus_And_Toolbars/Menu_XML_File.ui new file mode 100644 index 0000000..131fe04 --- /dev/null +++ b/Menus_And_Toolbars/Menu_XML_File.ui @@ -0,0 +1,17 @@ + + + + + File + + + Edit + + + Choices + + + Help + + + diff --git a/Menus_And_Toolbars/Popup_UI_File.xml b/Menus_And_Toolbars/Popup_UI_File.xml new file mode 100644 index 0000000..a314084 --- /dev/null +++ b/Menus_And_Toolbars/Popup_UI_File.xml @@ -0,0 +1,15 @@ + + + +
+ + About + app.about + + + Quit + app.quit + +
+
+
diff --git a/Menus_And_Toolbars/SimpleMenuBar.py b/Menus_And_Toolbars/SimpleMenuBar.py new file mode 100644 index 0000000..e938c53 --- /dev/null +++ b/Menus_And_Toolbars/SimpleMenuBar.py @@ -0,0 +1,61 @@ +#!/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_size_request(250, -1) + menubar = Gtk.MenuBar.new() + file = Gtk.MenuItem.new_with_label("File") + edit = Gtk.MenuItem.new_with_label("Edit") + help = Gtk.MenuItem.new_with_label("Help") + filemenu = Gtk.Menu.new() + editmenu = Gtk.Menu.new() + helpmenu = Gtk.Menu.new() + file.set_submenu(filemenu) + edit.set_submenu(editmenu) + help.set_submenu(helpmenu) + menubar.append(file) + menubar.append(edit) + menubar.append(help) + # Create the File menu content. + new = Gtk.MenuItem.new_with_label("New") + open = Gtk.MenuItem.new_with_label("Open") + filemenu.append(new) + filemenu.append(open) + # Create the Edit menu content. + cut = Gtk.MenuItem.new_with_label("Cut") + copy = Gtk.MenuItem.new_with_label("Copy") + paste = Gtk.MenuItem.new_with_label("Paste") + editmenu.append(cut) + editmenu.append(copy) + editmenu.append(paste) + # Create the Help menu content. + contents = Gtk.MenuItem.new_with_label("Help") + about = Gtk.MenuItem.new_with_label("About") + helpmenu.append(contents) + helpmenu.append(about) + self.add(menubar) + +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="Menu Bars") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/SimplePopupMenu.py b/Menus_And_Toolbars/SimplePopupMenu.py new file mode 100644 index 0000000..b527cf3 --- /dev/null +++ b/Menus_And_Toolbars/SimplePopupMenu.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + self.create_popup_menu(menu, progress) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + self.add(eventbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def create_popup_menu(self, menu, progress): + pulse = Gtk.MenuItem.new_with_label("Pulse Progress") + fill = Gtk.MenuItem.new_with_label("Set as Complete") + clear = Gtk.MenuItem.new_with_label("Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + pass + + def pulse_activated(self, item, progress): + pass + + def fill_activated(self, item, progress): + pass + + def clear_activated(self, item, progress): + pass + +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="Pop-up Menus") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/SimplePopupMenu2.py b/Menus_And_Toolbars/SimplePopupMenu2.py new file mode 100644 index 0000000..36cabe9 --- /dev/null +++ b/Menus_And_Toolbars/SimplePopupMenu2.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + self.create_popup_menu(menu, progress) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + self.add(eventbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def create_popup_menu(self, menu, progress): + pulse = Gtk.MenuItem.new_with_label("Pulse Progress") + fill = Gtk.MenuItem.new_with_label("Set as Complete") + clear = Gtk.MenuItem.new_with_label("Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: + menu.popup(None, None, None, None, event.button, event.time) + return True + return False + + def pulse_activated(self, item, progress): + progress.pulse() + progress.set_text("Pulse!") + + def fill_activated(self, item, progress): + progress.set_fraction(1.0) + progress.set_text("One Hundred Percent") + + def clear_activated(self, item, progress): + progress.set_fraction(0.0) + progress.set_text("Reset to Zero") + +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="Pop-up Menus") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/SimplePopupMenu3.py b/Menus_And_Toolbars/SimplePopupMenu3.py new file mode 100644 index 0000000..bda0b9c --- /dev/null +++ b/Menus_And_Toolbars/SimplePopupMenu3.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + progress.set_show_text(True) + self.create_popup_menu(menu, progress) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + self.add(eventbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def create_popup_menu(self, menu, progress): + group = Gtk.AccelGroup.new() + self.add_accel_group(group) + menu.set_accel_group(group) + pulse = Gtk.MenuItem.new_with_label("Pulse Progress") + fill = Gtk.MenuItem.new_with_label("Set as Complete") + clear = Gtk.MenuItem.new_with_label("Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + # Add the necessary keyboard accelerators. + pulse.add_accelerator("activate", group, Gdk.KEY_P, Gdk.ModifierType.CONTROL_MASK, + Gtk.AccelFlags.VISIBLE) + fill.add_accelerator("activate", group, Gdk.KEY_F, Gdk.ModifierType.CONTROL_MASK, + Gtk.AccelFlags.VISIBLE) + clear.add_accelerator("activate", group, Gdk.KEY_C, Gdk.ModifierType.CONTROL_MASK, + Gtk.AccelFlags.VISIBLE) + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: + menu.popup(None, None, None, None, event.button, event.time) + return True + return False + + def pulse_activated(self, item, progress): + progress.pulse() + progress.set_text("Pulse!") + + def fill_activated(self, item, progress): + progress.set_fraction(1.0) + progress.set_text("One Hundred Percent") + + def clear_activated(self, item, progress): + progress.set_fraction(0.0) + progress.set_text("Reset to Zero") + +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="Pop-up Menus") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/SimplePopupMenu4.py b/Menus_And_Toolbars/SimplePopupMenu4.py new file mode 100644 index 0000000..0813e82 --- /dev/null +++ b/Menus_And_Toolbars/SimplePopupMenu4.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppMenuItem(Gtk.MenuItem): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __setattr__(self, name, value): + self.__dict__[name] = value + + def __getattr__(self, name): + return self.__dict__[name] + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, -1) + # Create all of the necessary widgets and initialize the pop-up menu. + menu = Gtk.Menu.new() + eventbox = Gtk.EventBox.new() + progress = Gtk.ProgressBar.new() + progress.set_text("Nothing Yet Happened") + progress.set_show_text(True) + statusbar = Gtk.Statusbar.new() + self.create_popup_menu(menu, progress, statusbar) + progress.set_pulse_step(0.05) + eventbox.set_above_child(False) + eventbox.connect("button_press_event", self.button_press_event, menu) + eventbox.add(progress) + vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=0) + vbox.pack_start(eventbox, False, True, 0) + vbox.pack_start(statusbar, False, True, 0) + self.add(vbox) + eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) + eventbox.realize() + + def create_popup_menu(self, menu, progress, statusbar): + pulse = AppMenuItem(label="Pulse Progress") + fill = AppMenuItem(label="Set as Complete") + clear = AppMenuItem(label="Clear Progress") + separator = Gtk.SeparatorMenuItem.new() + pulse.connect("activate", self.pulse_activated, progress) + fill.connect("activate", self.fill_activated, progress) + clear.connect("activate", self.clear_activated, progress) + # Connect signals to each menu item for status bar messages. + pulse.connect("enter-notify-event", self.statusbar_hint, statusbar) + pulse.connect("leave-notify-event", self.statusbar_hint, statusbar) + fill.connect("enter-notify-event", self.statusbar_hint, statusbar) + fill.connect("leave-notify-event", self.statusbar_hint, statusbar) + clear.connect("enter-notify-event", self.statusbar_hint, statusbar) + clear.connect("leave-notify-event", self.statusbar_hint, statusbar) + pulse.__setattr__("menuhint", "Pulse the progress bar one step.") + fill.__setattr__("menuhint", "Set the progress bar to 100%.") + clear.__setattr__("menuhint", "Clear the progress bar to 0%.") + menu.append(pulse) + menu.append(separator) + menu.append(fill) + menu.append(clear) + menu.attach_to_widget(progress, None) + menu.show_all() + + def button_press_event(self, eventbox, event, menu): + if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: + menu.popup(None, None, None, None, event.button, event.time) + return True + return False + + def pulse_activated(self, item, progress): + progress.pulse() + progress.set_text("Pulse!") + + def fill_activated(self, item, progress): + progress.set_fraction(1.0) + progress.set_text("One Hundred Percent") + + def clear_activated(self, item, progress): + progress.set_fraction(0.0) + progress.set_text("Reset to Zero") + + def statusbar_hint(self, menuitem, event, statusbar): + id = statusbar.get_context_id("MenuItemHints") + if event.type == Gdk.EventType.ENTER_NOTIFY: + hint = menuitem.__getattr__("menuhint") + id = statusbar.push(id, hint) + elif event.type == Gdk.EventType.LEAVE_NOTIFY: + statusbar.pop(id) + return False + +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="Pop-up Menus") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/SimpleToolbar.py b/Menus_And_Toolbars/SimpleToolbar.py new file mode 100644 index 0000000..8adc0a3 --- /dev/null +++ b/Menus_And_Toolbars/SimpleToolbar.py @@ -0,0 +1,80 @@ +#!/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) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + toolbar = Gtk.Toolbar.new() + entry = Gtk.Entry.new() + vbox.pack_start(toolbar, True, False, 0) + vbox.pack_start(entry, True, False, 0) + self.create_toolbar(toolbar, entry) + self.add(vbox) + self.set_size_request(310, 75) + + def create_toolbar(self, toolbar, entry): + icon_theme = Gtk.IconTheme.get_default() + icon = icon_theme.load_icon("edit-cut", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + image = Gtk.Image.new_from_pixbuf(icon) + cut = Gtk.ToolButton.new(image, "Cut") + icon = icon_theme.load_icon("edit-copy", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + image = Gtk.Image.new_from_pixbuf(icon) + copy = Gtk.ToolButton.new(image, "Copy") + icon = icon_theme.load_icon("edit-paste", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + image = Gtk.Image.new_from_pixbuf(icon) + paste = Gtk.ToolButton.new(image, "Paste") + icon = icon_theme.load_icon("edit-select-all", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + image = Gtk.Image.new_from_pixbuf(icon) + selectall = Gtk.ToolButton.new(image, "Select All") + separator = Gtk.SeparatorToolItem.new() + toolbar.set_show_arrow(True) + toolbar.set_style(Gtk.ToolbarStyle.BOTH) + toolbar.insert(cut, 0) + toolbar.insert(copy, 1) + toolbar.insert(paste, 2) + toolbar.insert(separator, 3) + toolbar.insert(selectall, 4) + cut.connect("clicked", self.cut_clipboard, entry) + copy.connect("clicked", self.copy_clipboard, entry) + paste.connect("clicked", self.paste_clipboard, entry) + selectall.connect("clicked", self.select_all, entry) + + def cut_clipboard(self, button, entry): + entry.cut_clipboard() + + def copy_clipboard(self, button, entry): + entry.copy_clipboard() + + def paste_clipboard(self, button, entry): + entry.paste_clipboard() + + def select_all(self, button, entry): + entry.select_region(0, -1) + +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="Toolbar") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Menus_And_Toolbars/Toolbar_UI_File.xml b/Menus_And_Toolbars/Toolbar_UI_File.xml new file mode 100644 index 0000000..9c3b69c --- /dev/null +++ b/Menus_And_Toolbars/Toolbar_UI_File.xml @@ -0,0 +1,34 @@ + + + + + True + False + + + True + False + New Standard + app.newstandard + document-new + + + False + True + + + + + True + False + Quit + app.quit + application-exit + + + False + True + + + + diff --git a/More_Widgets/DrawingArea.py b/More_Widgets/DrawingArea.py new file mode 100644 index 0000000..e49129d --- /dev/null +++ b/More_Widgets/DrawingArea.py @@ -0,0 +1,121 @@ +#!/usr/bin/python3 + +import sys +import cairo +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +SIZE = 30 + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_size_request(450, 550) + drawingarea = Gtk.DrawingArea() + self.add(drawingarea) + drawingarea.connect('draw', self.draw) + + def triangle(self, ctx): + ctx.move_to(SIZE, 0) + ctx.rel_line_to(SIZE, 2 * SIZE) + ctx.rel_line_to(-2 * SIZE, 0) + ctx.close_path() + + def square(self, ctx): + ctx.move_to(0, 0) + ctx.rel_line_to(2 * SIZE, 0) + ctx.rel_line_to(0, 2 * SIZE) + ctx.rel_line_to(-2 * SIZE, 0) + ctx.close_path() + + def bowtie(self, ctx): + ctx.move_to(0, 0) + ctx.rel_line_to(2 * SIZE, 2 * SIZE) + ctx.rel_line_to(-2 * SIZE, 0) + ctx.rel_line_to(2 * SIZE, -2 * SIZE) + ctx.close_path() + + def inf(self, ctx): + ctx.move_to(0, SIZE) + ctx.rel_curve_to(0, SIZE, SIZE, SIZE, 2 * SIZE, 0) + ctx.rel_curve_to(SIZE, -SIZE, 2 * SIZE, -SIZE, 2 * SIZE, 0) + ctx.rel_curve_to(0, SIZE, -SIZE, SIZE, -2 * SIZE, 0) + ctx.rel_curve_to(-SIZE, -SIZE, -2 * SIZE, -SIZE, -2 * SIZE, 0) + ctx.close_path() + + def draw_shapes(self, ctx, x, y, fill): + ctx.save() + ctx.new_path() + ctx.translate(x + SIZE, y + SIZE) + self.bowtie(ctx) + if fill: + ctx.fill() + else: + ctx.stroke() + ctx.new_path() + ctx.translate(3 * SIZE, 0) + self.square(ctx) + if fill: + ctx.fill() + else: + ctx.stroke() + ctx.new_path() + ctx.translate(3 * SIZE, 0) + self.triangle(ctx) + if fill: + ctx.fill() + else: + ctx.stroke() + ctx.new_path() + ctx.translate(3 * SIZE, 0) + self.inf(ctx) + if fill: + ctx.fill() + else: + ctx.stroke() + ctx.restore() + + def fill_shapes(self, ctx, x, y): + self.draw_shapes(ctx, x, y, True) + + def stroke_shapes(self, ctx, x, y): + self.draw_shapes(ctx, x, y, False) + + def draw(self, da, ctx): + ctx.set_source_rgb(0, 0, 0) + ctx.set_line_width(SIZE / 4) + ctx.set_tolerance(0.1) + ctx.set_line_join(cairo.LINE_JOIN_ROUND) + ctx.set_dash([SIZE / 4.0, SIZE / 4.0], 0) + self.stroke_shapes(ctx, 0, 0) + ctx.set_dash([], 0) + self.stroke_shapes(ctx, 0, 3 * SIZE) + ctx.set_line_join(cairo.LINE_JOIN_BEVEL) + self.stroke_shapes(ctx, 0, 6 * SIZE) + ctx.set_line_join(cairo.LINE_JOIN_MITER) + self.stroke_shapes(ctx, 0, 9 * SIZE) + self.fill_shapes(ctx, 0, 12 * SIZE) + ctx.set_line_join(cairo.LINE_JOIN_BEVEL) + self.fill_shapes(ctx, 0, 15 * SIZE) + ctx.set_source_rgb(1, 0, 0) + self.stroke_shapes(ctx, 0, 15 * SIZE) + +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="Drawing Areas") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/More_Widgets/GtkEntryCompletion.py b/More_Widgets/GtkEntryCompletion.py new file mode 100644 index 0000000..2dd356d --- /dev/null +++ b/More_Widgets/GtkEntryCompletion.py @@ -0,0 +1,47 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + widgets = ["GtkDialog", "GtkWindow", "GtkContainer", "GtkWidget"] + self.set_border_width(10) + label = Gtk.Label.new("Enter a widget in the following GtkEntry:") + entry = Gtk.Entry.new() + # Create a GtkListStore that will hold autocompletion possibilities. + types = (GObject.TYPE_STRING,) + store = Gtk.ListStore.new(types) + for widget in widgets: + iter = store.append() + store.set(iter, 0, widget) + completion = Gtk.EntryCompletion.new() + entry.set_completion(completion) + completion.set_model(store) + completion.set_text_column(0) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + vbox.pack_start(label, False, False, 0) + vbox.pack_start(entry, 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="Automatic Completion") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/More_Widgets/GtkPrintOperation.py b/More_Widgets/GtkPrintOperation.py new file mode 100644 index 0000000..be8e744 --- /dev/null +++ b/More_Widgets/GtkPrintOperation.py @@ -0,0 +1,141 @@ +#!/usr/bin/python3 + +import sys +import math +from os.path import expanduser +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('PangoCairo', '1.0') +from gi.repository import Gtk, cairo, Pango, PangoCairo + +class Widgets: + + def __init__(self): + self.window = None + self.chooser = None + self.data = None + self.settings = Gtk.PrintSettings.new() + +class PrintData: + + def __init__(self): + self.filename = None + self.fontsize = None + self.lines_per_page = None + self.lines = None + self.total_lines = None + self.total_pages = None + + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.HEADER_HEIGHT = 20.0 + self.HEADER_GAP = 8.5 + w = Widgets() + w.window = self + self.set_border_width(10) + w.chooser = Gtk.FileChooserButton.new ("Select a File", + Gtk.FileChooserAction.OPEN) + w.chooser.set_current_folder(expanduser("~")) + print = Gtk.Button.new_with_label("Print") + print.connect("clicked", self.print_file, w) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(w.chooser, False, False, 0) + hbox.pack_start(print, False, False, 0) + self.add(hbox) + + def print_file(self, button, w): + filename = w.chooser.get_filename() + if filename == None: + return + operation = Gtk.PrintOperation.new() + if w.settings != None: + operation.set_print_settings(w.settings) + w.data = PrintData() + w.data.filename = filename + w.data.font_size = 10.0 + operation.connect("begin_print", self.begin_print, w) + operation.connect("draw_page", self.draw_page, w) + operation.connect("end_print", self.end_print, w) + res = operation.run(Gtk.PrintOperationAction.PRINT_DIALOG, + w.window) + if res == Gtk.PrintOperationResult.APPLY: + if w.settings != None: + w.settings = None + settings = operation.get_print_settings() + elif res == Gtk.PrintOperationResult.ERROR: + dialog = Gtk.MessageDialog.new(w.window, + Gtk.DialogFlags.DESTROY_WITH_PARENT, + Gtk.MessageType.ERROR, + Gtk.ButtonsType.S_CLOSE, + "Print operation error.") + dialog.run() + dialog.destroy() + + def begin_print(self, operation, context, w): + w.data.lines = [] + f = open(w.data.filename) + for line in f: + w.data.lines.append(line) + f.close() + w.data.total_lines = len(w.data.lines) + height = context.get_height() - self.HEADER_HEIGHT - self.HEADER_GAP + w.data.lines_per_page = math.floor(height / (w.data.font_size + 3)) + w.data.total_pages = (w.data.total_lines - 1) / w.data.lines_per_page + 1 + operation.set_n_pages(w.data.total_pages) + + def draw_page(self, operation, context, page_nr, w): + cr = context.get_cairo_context() + width = context.get_width() + layout = context.create_pango_layout() + desc = Pango.font_description_from_string("Monospace") + desc.set_size(w.data.font_size * Pango.SCALE) + layout.set_font_description(desc) + layout.set_text(w.data.filename, -1) + layout.set_width(-1) + layout.set_alignment(Pango.Alignment.LEFT) + (width, height) = layout.get_size() + text_height = height / Pango.SCALE + cr.move_to(0, (self.HEADER_HEIGHT - text_height) / 2) + PangoCairo.show_layout(cr, layout) + page_str = "%d of %d" % (page_nr + 1, w.data.total_pages) + layout.set_text(page_str, -1) + (width, height) = layout.get_size() + layout.set_alignment(Pango.Alignment.RIGHT) + cr.move_to(width - (width / Pango.SCALE), + (self.HEADER_HEIGHT - text_height) / 2) + PangoCairo.show_layout(cr, layout) + cr.move_to(0, self.HEADER_HEIGHT + self.HEADER_GAP) + line = page_nr * w.data.lines_per_page + #for (i = 0; i < w.data.lines_per_page && line < w.data.total_lines; i++) + i = 0 + while i < w.data.lines_per_page and line < w.data.total_lines: + layout.set_text(w.data.lines[line], -1) + PangoCairo.show_layout(cr, layout) + cr.rel_move_to(0, w.data.font_size + 3) + line += 1 + i += 1 + + def end_print(self, operation, context, w): + w.data.lines = None + w.data = None + +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="Calendar") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/More_Widgets/RecentFileChooserDialog.py b/More_Widgets/RecentFileChooserDialog.py new file mode 100644 index 0000000..36ef9fd --- /dev/null +++ b/More_Widgets/RecentFileChooserDialog.py @@ -0,0 +1,170 @@ +#!/usr/bin/python3 + +import sys +import urllib +from urllib.request import pathname2url +import os +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Pango + +class Widgets(): + + def __init__(self): + self.window = None + self.textview = None + self.recent = None + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + w = Widgets() + w.window = self + self.set_border_width(5) + self.set_size_request(600, 400) + w.textview = Gtk.TextView.new() + fd = Pango.font_description_from_string("Monospace 10") + self.modify_font(fd) + swin = Gtk.ScrolledWindow.new(None, None) + openbutton = Gtk.Button.new_with_label("open") + save = Gtk.Button.new_with_label("Save") + icon_theme = Gtk.IconTheme.get_default() + icon = icon_theme.load_icon("document-open", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + image = Gtk.Image.new_from_pixbuf(icon) + w.recent = Gtk.MenuToolButton.new(image, "Recent Files") + manager = Gtk.RecentManager.get_default() + menu = Gtk.RecentChooserMenu.new_for_manager(manager) + w.recent.set_menu(menu) + menu.set_show_not_found(False) + menu.set_local_only(True) + menu.set_limit(10) + menu.set_sort_type(Gtk.RecentSortType.MRU) + menu.connect("selection-done", self.menu_activated, w) + openbutton.connect("clicked", self.open_file, w) + save.connect("clicked", self.save_file, w) + w.recent.connect("clicked", self.open_recent_file, w) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(openbutton, False, False, 0) + hbox.pack_start(save, False, False, 0) + hbox.pack_start(w.recent, False, False, 0) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + swin.add(w.textview) + vbox.pack_start(hbox, False, False, 0) + vbox.pack_start(swin, True, True, 0) + w.window.add(vbox) + + def save_file(self, save, w): + filename = w.window.get_title() + buffer = w.textview.get_buffer() + (start, end) = buffer.get_bounds() + content = buffer.get_text(start, end, False) + f = open(filename, 'w') + f.write(content) + f.close() + + def menu_activated(self, menu, w): + filename = menu.get_current_uri() + if filename != None: + fn = os.path.basename(filename) + f = open(fn, 'r') + contents = f.read() + f.close() + w.window.set_title(fn) + buffer = w.textview.get_buffer() + buffer.set_text(content, -1) + else: + print("The file '%s' could not be read!" % filename) + + def open_file(self, openbutton, w): + dialog = Gtk.FileChooserDialog(title="Open File", parent=w.window, + action=Gtk.FileChooserAction.OPEN, + buttons=("Cancel", + Gtk.ResponseType.CANCEL, + "Open", + Gtk.ResponseType.OK)) + if dialog.run() == Gtk.ResponseType.OK: + filename = dialog.get_filename() + content = "" + f = open(filename, 'r') + content = f.read() + f.close() + if len(content) > 0: + # Create a new recently used resource. + data = Gtk.RecentData() + data.display_name = None + data.description = None + data.mime_type = "text/plain" + data.app_name = os.path.basename(__file__) + data.app_exec = " " + data.app_name + "%u" + #data.groups = ["testapp", None] + data.is_private = False + url = pathname2url(filename) + # Add the recently used resource to the default recent manager. + manager = Gtk.RecentManager.get_default() + result = manager.add_full(url, data) + # Load the file and set the filename as the title of the window. + w.window.set_title(filename) + buffer = w.textview.get_buffer() + buffer.set_text(content, -1) + dialog.destroy() + + def open_recent_file(self, recent, w): + manager = Gtk.RecentManager.get_default() + dialog = Gtk.RecentChooserDialog(title="Open Recent File", + parent=w.window, + recent_manager=manager, + buttons=("Cancel", + Gtk.ResponseType.CANCEL, + "Open", + Gtk.ResponseType.OK)) + # Add a filter that will display all of the files in the dialog. + filter = Gtk.RecentFilter.new() + filter.set_name("All Files") + filter.add_pattern("*") + dialog.add_filter(filter) + # Add another filter that will only display plain text files. + filter = Gtk.RecentFilter.new() + filter.set_name("Plain Text") + filter.add_mime_type("text/plain") + dialog.add_filter(filter) + dialog.set_show_not_found(False) + dialog.set_local_only(True) + dialog.set_limit(10) + dialog.set_sort_type(Gtk.RecentSortType.MRU) + if dialog.run() == Gtk.ResponseType.OK: + filename = dialog.get_current_uri() + if filename != None: + # Remove the "file://" prefix from the beginning of the + # URI if it exists. + content = "" + fn = os.path.basename(filename) + f = open(fn, 'r') + contents = f.read() + f.close() + if len(content) > 0: + w.window.set_title(fn) + buffer = w.textview.get_buffer() + buffer.set_text(content, -1) + else: + print("The file '%s' could not be read!" % filename) + dialog.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="Recent Files") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/README.md b/README.md new file mode 100644 index 0000000..3384cfa --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Apress Source Code + +This repository accompanies [*Foundations of PyGTK Development*](https://www.apress.com/9781484241783) by W. David Ashley and Andrew Krause (Apress, 2019). + +[comment]: #cover +![Cover image](9781484241783.jpg) + +Download the files as a zip using the green button, or clone the repository to your machine using Git. + +## Releases + +Release v1.0 corresponds to the code in the published book, without corrections or updates. + +## Contributions + +See the file Contributing.md for more information on how you can contribute to this repository. \ No newline at end of file diff --git a/Simple_Applications/Exercise_1.py b/Simple_Applications/Exercise_1.py new file mode 100644 index 0000000..5c56e24 --- /dev/null +++ b/Simple_Applications/Exercise_1.py @@ -0,0 +1,41 @@ +#!/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) + label = Gtk.Label.new("Bunny") + label.set_selectable(True) + self.add(label) + self.set_size_request(300, 100) + self.set_resizable(False) + self.connect("key-press-event", self.on_window_keypress, label) + + def on_window_keypress(self, window, event, label): + tmp = label.get_text() + label.set_text(self.get_title()) + self.set_title(tmp) + return False + +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="Bugs") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Simple_Applications/HelloWorld.py b/Simple_Applications/HelloWorld.py new file mode 100644 index 0000000..65ec7f6 --- /dev/null +++ b/Simple_Applications/HelloWorld.py @@ -0,0 +1,27 @@ +#!/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) + +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="Main Window") + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Simple_Applications/HelloWorld_With_Button.py b/Simple_Applications/HelloWorld_With_Button.py new file mode 100644 index 0000000..ce5acc5 --- /dev/null +++ b/Simple_Applications/HelloWorld_With_Button.py @@ -0,0 +1,38 @@ +#!/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(25) + button = Gtk.Button.new_with_mnemonic("_Close") + button.connect("clicked", self.on_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + self.add(button) + self.set_size_request(200, 100) + + def on_button_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="Hello World!") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Simple_Applications/HelloWorld_With_Label.py b/Simple_Applications/HelloWorld_With_Label.py new file mode 100644 index 0000000..e3e4362 --- /dev/null +++ b/Simple_Applications/HelloWorld_With_Label.py @@ -0,0 +1,33 @@ +#!/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) + label = Gtk.Label.new("Hello World!") + label.set_selectable(True) + self.add(label) + self.set_size_request(200, 100) + +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="Hello World!") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/Exercise_1.py b/Text_View_Widget/Exercise_1.py new file mode 100644 index 0000000..942f62a --- /dev/null +++ b/Text_View_Widget/Exercise_1.py @@ -0,0 +1,165 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(300, -1) + + textview = Gtk.TextView.new() + search = Gtk.Entry.new() + search.set_text("Search for ...") + + new = Gtk.Button.new_with_label("New") + openb = Gtk.Button.new_with_label("Open") + save = Gtk.Button.new_with_label("Save") + cut = Gtk.Button.new_with_label("Cut") + copy = Gtk.Button.new_with_label("Copy") + paste = Gtk.Button.new_with_label("Paste") + find = Gtk.Button.new_with_label("Find") + + new.connect("clicked", self.on_new_clicked, textview) + openb.connect("clicked", self.on_open_clicked, textview) + save.connect("clicked", self.on_save_clicked, textview) + cut.connect("clicked", self.on_cut_clicked, textview) + copy.connect("clicked", self.on_copy_clicked, textview) + paste.connect("clicked", self.on_paste_clicked, textview) + find.connect("clicked", self.on_find_clicked, textview, search) + + scrolled_win = Gtk.ScrolledWindow.new() + scrolled_win.add(textview) + + vbox1 = Gtk.Box.new (Gtk.Orientation.VERTICAL, 5) + vbox1.pack_start(new, False, False, 0) + vbox1.pack_start(openb, False, False, 0) + vbox1.pack_start(save, False, False, 0) + vbox1.pack_start(cut, False, False, 0) + vbox1.pack_start(copy, False, False, 0) + vbox1.pack_start(paste, False, False, 0) + + searchbar = Gtk.Box.new (Gtk.Orientation.HORIZONTAL, 5) + searchbar.pack_start(search, False, False, 0) + searchbar.pack_start(find, False, False, 0) + + hbox1 = Gtk.Box.new (Gtk.Orientation.HORIZONTAL, 5) + hbox1.pack_start(scrolled_win, True, True, 0) + hbox1.pack_start(vbox1, False, False, 0) + + vbox2 = Gtk.Box.new (Gtk.Orientation.VERTICAL, 5) + vbox2.pack_start(hbox1, True, True, 0) + vbox2.pack_start(searchbar, False, False, 0) + + self.add(vbox2) + self.show_all() + + def on_new_clicked(self, button, textview): + dialog = Gtk.MessageDialog(title="Question", parent=self, + flags=Gtk.DialogFlags.MODAL) + dialog.set_border_width(10) + dialog.add_button("Yes", Gtk.ResponseType.YES) + dialog.add_button("No", Gtk.ResponseType.NO) + dialog.props.text = "All changes will be lost.\nDo you want to continue?" + dialog.show_all() + response = dialog.run() + if response == Gtk.ResponseType.YES: + buffer = textview.get_buffer() + buffer.set_text("") + dialog.destroy() + + def on_open_clicked(self, button, textview): + dialog = Gtk.FileChooserDialog(title="Choose a file ..", + parent=self, + flags=Gtk.FileChooserAction.OPEN, + buttons=("Open", Gtk.ResponseType.APPLY, + "Cancel", Gtk.ResponseType.CANCEL)) + dialog.show_all() + response = dialog.run() + if response == Gtk.ResponseType.APPLY: + buffer = textview.get_buffer() + file = dialog.get_filename() + f = open(file, 'r') + content = f.read() + f.close() + buffer.set_text(content) + dialog.destroy() + + def on_save_clicked(self, button, textview): + dialog = Gtk.FileChooserDialog(title="Save the file ..", + parent=self, + flags=Gtk.FileChooserAction.SAVE, + buttons=("Save", Gtk.ResponseType.APPLY, + "Cancel", Gtk.ResponseType.CANCEL)) + response = dialog.run() + if response == Gtk.ResponseType.APPLY: + file = dialog.get_filename() + buffer = textview.get_buffer() + (start, end) = buffer.get_bounds() + content = buffer.get_text(start, end, True) + f = open(file, 'w') + f.write(content) + f.close() + dialog.destroy() + + def on_cut_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + buffer = textview.get_buffer() + buffer.cut_clipboard(clipboard, True) + + def on_copy_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + buffer = textview.get_buffer() + buffer.copy_clipboard(clipboard) + + def on_paste_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + buffer = textview.get_buffer() + buffer.paste_clipboard(clipboard, None, True) + + def on_find_clicked(self, button, textview, search): + find = search.get_text() + buffer = textview.get_buffer() + cursorpos = buffer.props.cursor_position + start = buffer.get_iter_at_offset(cursorpos) + end = buffer.get_iter_at_offset(-1) + if start.compare(end) != 0: + start.forward_char() + success = start.forward_search(find, 0, None) + if success != None and len(success) != 0: + (start, end) = success + mark = buffer.create_mark(None, start, False) + textview.scroll_mark_onscreen(mark) + buffer.delete_mark(mark) + buffer.select_range(start, end) + else: + dialog = Gtk.MessageDialog(title="Information", parent=self, + flags=Gtk.DialogFlags.MODAL) + dialog.set_border_width(10) + dialog.add_button("Ok", Gtk.ResponseType.OK) + dialog.props.text = "The text was not found!" + dialog.show_all() + dialog.run() + dialog.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="Exercise 1") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextBuffer_Child.py b/Text_View_Widget/GtkTextBuffer_Child.py new file mode 100644 index 0000000..3bf5e3f --- /dev/null +++ b/Text_View_Widget/GtkTextBuffer_Child.py @@ -0,0 +1,49 @@ +#!/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(25) + self.set_border_width(10) + self.set_size_request(250, 100) + textview = Gtk.TextView.new() + buffer = textview.get_buffer() + text = "\n Click to exit!" + buffer.set_text(text, len(text)) + iter = buffer.get_iter_at_offset(8) + anchor = buffer.create_child_anchor(iter) + button = Gtk.Button.new_with_label("the button") + button.connect("clicked", self.on_button_clicked) + button.set_relief(Gtk.ReliefStyle.NORMAL) + textview.add_child_at_anchor(button, anchor) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.add(textview) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.ALWAYS) + self.add(scrolled_win) + + def on_button_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="Child Widgets") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextBuffer_Formatted.py b/Text_View_Widget/GtkTextBuffer_Formatted.py new file mode 100644 index 0000000..a2ae50d --- /dev/null +++ b/Text_View_Widget/GtkTextBuffer_Formatted.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Pango + +text_to_scales = [("Quarter Sized", 0.25), + ("Double Extra Small", 0.5787037037037), + ("Extra Small", 0.6444444444444), + ("Small", 0.8333333333333), + ("Medium", 1.0), ("Large", 1.2), + ("Extra Large", 1.4399999999999), + ("Double Extra Large", 1.728), + ("Double Sized", 2.0)] + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(500, -1) + textview = Gtk.TextView.new() + buffer = textview.get_buffer() + buffer.create_tag("bold", weight=Pango.Weight.BOLD) + buffer.create_tag("italic", style=Pango.Style.ITALIC) + buffer.create_tag("strike", strikethrough=True) + buffer.create_tag("underline", underline=Pango.Underline.SINGLE) + bold = Gtk.Button.new_with_label("Bold") + italic = Gtk.Button.new_with_label("Italic") + strike = Gtk.Button.new_with_label("Strike") + underline = Gtk.Button.new_with_label("Underline") + clear = Gtk.Button.new_with_label("Clear") + scale_button = Gtk.ComboBoxText.new() + i = 0 + while i < len(text_to_scales): + (name, scale) = text_to_scales[i] + scale_button.append_text(name) + buffer.create_tag(tag_name=name, scale=scale) + i += 1 + bold.__setattr__("tag", "bold") + italic.__setattr__("tag", "italic") + strike.__setattr__("tag", "strike") + underline.__setattr__("tag", "underline") + bold.connect("clicked", self.on_format, textview) + italic.connect("clicked", self.on_format, textview) + strike.connect("clicked", self.on_format, textview) + underline.connect("clicked", self.on_format, textview) + clear.connect("clicked", self.on_clear_clicked, textview) + scale_button.connect("changed", self.on_scale_changed, textview) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.pack_start(bold, False, False, 0) + vbox.pack_start(italic, False, False, 0) + vbox.pack_start(strike, False, False, 0) + vbox.pack_start(underline, False, False, 0) + vbox.pack_start(scale_button, False, False, 0) + vbox.pack_start(clear, False, False, 0) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.add(textview) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.ALWAYS) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(scrolled_win, True, True, 0) + hbox.pack_start(vbox, False, True, 0) + self.add(hbox) + + def on_format(self, button, textview): + tagname = button.tag + buffer = textview.get_buffer() + (start, end) = buffer.get_selection_bounds() + buffer.apply_tag_by_name(tagname, start, end) + + def on_scale_changed(self, button, textview): + if button.get_active() == -1: + return + text = button.get_active_text() + button.__setattr__("tag", text) + self.on_format(button, textview) + button.set_active(-1) + + def on_clear_clicked(self, button, textview): + buffer = textview.get_buffer() + (start, end) = buffer.get_selection_bounds() + buffer.remove_all_tags(start, end) + +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="Text Tags") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextBuffer_Images.py b/Text_View_Widget/GtkTextBuffer_Images.py new file mode 100644 index 0000000..0ead046 --- /dev/null +++ b/Text_View_Widget/GtkTextBuffer_Images.py @@ -0,0 +1,46 @@ +#!/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(200, 150) + textview = Gtk.TextView.new() + buffer = textview.get_buffer() + text = " Undo\n Redo" + buffer.set_text(text, len(text)) + icon_theme = Gtk.IconTheme.get_default() + undo = icon_theme.load_icon("edit-undo", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + line = buffer.get_iter_at_line (0) + buffer.insert_pixbuf(line, undo) + redo = icon_theme.load_icon("edit-redo", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + line = buffer.get_iter_at_line (1) + buffer.insert_pixbuf(line, redo) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.add(textview) + self.add (scrolled_win) + +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="Pixbufs") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextBuffer_Search.py b/Text_View_Widget/GtkTextBuffer_Search.py new file mode 100644 index 0000000..62c987a --- /dev/null +++ b/Text_View_Widget/GtkTextBuffer_Search.py @@ -0,0 +1,70 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + textview = Gtk.TextView.new() + entry = Gtk.Entry.new() + entry.set_text("Search for ...") + find = Gtk.Button.new_with_label("Find") + find.connect("clicked", self.on_find_clicked, (textview, entry)) + scrolled_win = Gtk.ScrolledWindow.new (None, None) + scrolled_win.set_size_request(250, 200) + scrolled_win.add(textview) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(entry, True, True, 0) + hbox.pack_start(find, True, True, 0) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.pack_start(scrolled_win, True, True, 0) + vbox.pack_start(hbox, True, True, 0) + self.add(vbox) + + def on_find_clicked(self, button, w): + find = w[1].get_text() + find_len = len(find) + buffer = w[0].get_buffer() + start = buffer.get_start_iter() + end_itr = buffer.get_end_iter() + i = 0 + while True: + end = start.copy() + end.forward_chars(find_len) + text = buffer.get_text(start, end, False) + if text == find: + i += 1 + start.forward_chars(find_len) + else: + start.forward_char() + if end.compare(end_itr) == 0: + break + output = "The string '"+find+"' was found " + str(i) + " times!" + dialog = Gtk.MessageDialog(parent=self, flags=Gtk.DialogFlags.MODAL, + message_type=Gtk.MessageType.INFO, + text=output, title="Information", + buttons=("OK", Gtk.ResponseType.OK)) + dialog.run() + dialog.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="Searching Buffers") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextView.py b/Text_View_Widget/GtkTextView.py new file mode 100644 index 0000000..0612081 --- /dev/null +++ b/Text_View_Widget/GtkTextView.py @@ -0,0 +1,37 @@ +#!/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, 150) + textview = Gtk.TextView.new() + buffer = textview.get_buffer() + text = "Your 1st GtkTextView widget!" + buffer.set_text(text, len(text)) + scrolled_win = Gtk.ScrolledWindow.new (None, None) + scrolled_win.add(textview) + self.add(scrolled_win) + +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="Text Views") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextViewProperties.py b/Text_View_Widget/GtkTextViewProperties.py new file mode 100644 index 0000000..31ad262 --- /dev/null +++ b/Text_View_Widget/GtkTextViewProperties.py @@ -0,0 +1,51 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Pango + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 150) + font = Pango.font_description_from_string("Monospace Bold 10") + textview = Gtk.TextView.new() + textview.modify_font(font) + textview.set_wrap_mode(Gtk.WrapMode.WORD) + textview.set_justification(Gtk.Justification.RIGHT) + textview.set_editable(True) + textview.set_cursor_visible(True) + textview.set_pixels_above_lines(5) + textview.set_pixels_below_lines(5) + textview.set_pixels_inside_wrap(5) + textview.set_left_margin(10) + textview.set_right_margin(10) + buffer = textview.get_buffer() + text = "This is some text!\nChange me!\nPlease!" + buffer.set_text(text, len(text)) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.ALWAYS) + scrolled_win.add(textview) + self.add(scrolled_win) + +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="Text Views Properties") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/GtkTextView_Menu.py b/Text_View_Widget/GtkTextView_Menu.py new file mode 100644 index 0000000..28bcd80 --- /dev/null +++ b/Text_View_Widget/GtkTextView_Menu.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + textview = Gtk.TextView.new() + cut = Gtk.Button.new_with_label("Cut") + copy = Gtk.Button.new_with_label("Copy") + paste = Gtk.Button.new_with_label("Paste") + cut.connect("clicked", self.on_cut_clicked, textview) + copy.connect("clicked", self.on_copy_clicked, textview) + paste.connect("clicked", self.on_paste_clicked, textview) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_size_request(300, 200) + scrolled_win.add(textview) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(cut, True, True, 0) + hbox.pack_start(copy, True, True, 0) + hbox.pack_start(paste, True, True, 0) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.pack_start(scrolled_win, True, True, 0) + vbox.pack_start(hbox, True, True, 0) + self.add(vbox) + + def on_cut_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.Atom.intern("CLIPBOARD", False)) + buffer = textview.get_buffer() + buffer.cut_clipboard(clipboard, True) + + def on_copy_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.Atom.intern("CLIPBOARD", False)) + buffer = textview.get_buffer() + buffer.copy_clipboard(clipboard) + + def on_paste_clicked(self, button, textview): + clipboard = Gtk.Clipboard.get(Gdk.Atom.intern("CLIPBOARD", False)) + buffer = textview.get_buffer() + buffer.paste_clipboard (clipboard, None, 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="Cut, Copy & Paste") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/ScrolledWindowAndViewport.py b/Text_View_Widget/ScrolledWindowAndViewport.py new file mode 100644 index 0000000..90bc6bb --- /dev/null +++ b/Text_View_Widget/ScrolledWindowAndViewport.py @@ -0,0 +1,73 @@ +#!/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) + grid1 = Gtk.Grid.new() + grid2 = Gtk.Grid.new() + grid1.set_column_homogeneous = True + grid2.set_column_homogeneous = True + grid1.set_row_homogeneous = True + grid2.set_row_homogeneous = True + grid1.set_column_spacing = 5 + grid2.set_column_spacing = 5 + grid1.set_row_spacing = 5 + grid2.set_row_spacing = 5 + i = 0 + while i < 10: + j = 0 + while j < 10: + button = Gtk.Button.new_with_label("Close") + button.set_relief(Gtk.ReliefStyle.NONE) + button.connect("clicked", self.on_button_clicked) + grid1.attach(button, i, j, 1, 1) + button = Gtk.Button.new_with_label("Close") + button.set_relief(Gtk.ReliefStyle.NONE) + button.connect("clicked", self.on_button_clicked) + grid2.attach(button, i, j, 1, 1) + j += 1 + i += 1 + swin = Gtk.ScrolledWindow.new(None, None) + horizontal = swin.get_hadjustment() + vertical = swin.get_vadjustment() + viewport = Gtk.Viewport.new(horizontal, vertical) + swin.set_border_width(5) + swin.set_propagate_natural_width(True) + swin.set_propagate_natural_height(True) + viewport.set_border_width(5) + swin.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + swin.add_with_viewport(grid1) + viewport.add(grid2) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.set_homogeneous = True + vbox.pack_start(viewport, True, True, 5) + vbox.pack_start(swin, True, True, 5) + self.add (vbox) + self.show_all() + + def on_button_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="Scrolled Windows & Viewports") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Text_View_Widget/UsingGtkTextView.py b/Text_View_Widget/UsingGtkTextView.py new file mode 100644 index 0000000..974b9a1 --- /dev/null +++ b/Text_View_Widget/UsingGtkTextView.py @@ -0,0 +1,60 @@ +#!/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(-1, -1) + textview = Gtk.TextView.new() + entry = Gtk.Entry.new() + insert_button = Gtk.Button.new_with_label("Insert Text") + retrieve = Gtk.Button.new_with_label("Get Text") + insert_button.connect("clicked", self.on_insert_text, (entry, textview)) + retrieve.connect("clicked", self.on_retrieve_text, (entry, textview)) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.add(textview) + hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 5) + hbox.pack_start(entry, True, True, 0) + hbox.pack_start(insert_button, True, True, 0) + hbox.pack_start(retrieve, True, True, 0) + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 5) + vbox.pack_start(scrolled_win, True, True, 0) + vbox.pack_start(hbox, True, True, 0) + self.add(vbox) + self.show_all() + + def on_insert_text(self, button, w): + buffer = w[1].get_buffer() + text = w[0].get_text() + mark = buffer.get_insert() + iter = buffer.get_iter_at_mark(mark) + buffer.insert(iter, text, len(text)) + + def on_retrieve_text(self, button, w): + buffer = w[1].get_buffer() + (start, end) = buffer.get_selection_bounds() + text = buffer.get_text(start, end, False) + print(text) + +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="Text Iterators") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/AccelCellRenderers.py b/Tree_View_Widget/AccelCellRenderers.py new file mode 100644 index 0000000..a272644 --- /dev/null +++ b/Tree_View_Widget/AccelCellRenderers.py @@ -0,0 +1,73 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk, GObject + +ACTION = 0 +MASK = 1 +VALUE = 2 + +list = [( "Cut", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_X ), + ( "Copy", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_C ), + ( "Paste", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_V ), + ( "New", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_N ), + ( "Open", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_O ), + ( "Print", Gdk.ModifierType.CONTROL_MASK, Gdk.KEY_P )] + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_size_request(250, 250) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_INT, + GObject.TYPE_UINT) + for row in list: + (action, mask, value) = row + iter = store.append(None) + store.set(iter, ACTION, action, MASK, mask, VALUE, value) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererAccel() + column = Gtk.TreeViewColumn("Action", renderer, text=ACTION) + treeview.append_column(column) + renderer = Gtk.CellRendererAccel(accel_mode=Gtk.CellRendererAccelMode.GTK, + editable=True) + column = Gtk.TreeViewColumn("Key", renderer, accel_mods=MASK, + accel_key=VALUE) + treeview.append_column(column) + renderer.connect("accel_edited", self.accel_edited, treeview) + + def accel_edited(self, renderer, path, accel_key, mask, + hardware_keycode, treeview): + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, MASK, mask, VALUE, accel_key) + +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="Accelerator Keys") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/ComboBoxCellRenderers.py b/Tree_View_Widget/ComboBoxCellRenderers.py new file mode 100644 index 0000000..8684f7a --- /dev/null +++ b/Tree_View_Widget/ComboBoxCellRenderers.py @@ -0,0 +1,32 @@ + def setup_tree_view(self, treeview): + # Create a GtkListStore that will be used for the combo box renderer. + model = Gtk.ListStore.new((GObject.TYPE_STRING, + GObject.TYPE_STRING)) + iter = model.append() + model.set(iter, 0, "None") + iter = model.append() + model.set(iter, 0, "One") + iter = model.append() + model.set(iter, 0, "Half a Dozen") + iter = model.append() + model.set(iter, 0, "Dozen") + iter = model.append() + model.set(iter, 0, "Two Dozen") + # Create the GtkCellRendererCombo and add the tree model. Then, add the + # renderer to a new column and add the column to the GtkTreeView. + renderer = Gtk.CellRendererCombo(text_column=0, editable=True, + has_entry=True, model=model) + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer.connect("edited", self.cell_edited, treeview) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + def cell_edited(self, renderer, path, new_text, treeview): + # Make sure the text is not empty. If not, apply it to the tree view cell. + if new_text != "": + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, QUANTITY, new_text) diff --git a/Tree_View_Widget/Edit_GroceryList.py b/Tree_View_Widget/Edit_GroceryList.py new file mode 100644 index 0000000..2cd9787 --- /dev/null +++ b/Tree_View_Widget/Edit_GroceryList.py @@ -0,0 +1,172 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +BUY_IT = 0 +QUANTITY = 1 +PRODUCT = 2 + +PRODUCT_CATEGORY = 0 +PRODUCT_CHILD = 1 + +GroceryItem = (( PRODUCT_CATEGORY, True, 0, "Cleaning Supplies"), + ( PRODUCT_CHILD, True, 1, "Paper Towels" ), + ( PRODUCT_CHILD, True, 3, "Toilet Paper" ), + ( PRODUCT_CATEGORY, True, 0, "Food"), + ( PRODUCT_CHILD, True, 2, "Bread" ), + ( PRODUCT_CHILD, False, 1, "Butter" ), + ( PRODUCT_CHILD, True, 1, "Milk" ), + ( PRODUCT_CHILD, False, 3, "Chips" ), + ( PRODUCT_CHILD, True, 4, "Soda" )) + +class AddDialog(Gtk.Dialog): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + parent = kwargs['parent'] + # set up buttons + self.add_button("Add", Gtk.ResponseType.OK) + self.add_button("Cancel", Gtk.ResponseType.CANCEL) + # set up dialog widgets + combobox = Gtk.ComboBoxText.new() + entry = Gtk.Entry.new() + spin = Gtk.SpinButton.new_with_range(0, 100, 1) + check = Gtk.CheckButton.new_with_mnemonic("_Buy the Product") + spin.set_digits(0) + # Add all of the categories to the combo box. + for row in GroceryItem: + (ptype, buy, quant, prod) = row + if ptype == PRODUCT_CATEGORY: + combobox.append_text(prod) + # create a grid + grid = Gtk.Grid.new() + grid.set_row_spacing (5) + grid.set_column_spacing(5) + # fill out the grid + grid.attach(Gtk.Label.new("Category:"), 0, 0, 1, 1) + grid.attach(Gtk.Label.new("Product:"), 0, 1, 1, 1) + grid.attach(Gtk.Label.new("Quantity:"), 0, 2, 1, 1) + grid.attach(combobox, 1, 0, 1, 1) + grid.attach(entry, 1, 1, 1, 1) + grid.attach(spin, 1, 2, 1, 1) + grid.attach(check, 1, 3, 1, 1) + self.get_content_area().pack_start(grid, True, True, 5) + self.show_all() + # run the dialog and check the results + if self.run() != Gtk.ResponseType.OK: + self.destroy() + return + quantity = spin.get_value() + product = entry.get_text() + category = combobox.get_active_text() + buy = check.get_active() + if product == "" or category == None: + print("All of the fields were not correctly filled out!") + return + model = parent.get_treeview().get_model(); + iter = model.get_iter_from_string("0") + # Retrieve an iterator pointing to the selected category. + while iter: + (name,) = model.get(iter, PRODUCT) + if name.lower() == category.lower(): + break + iter = model.iter_next(iter) + # Convert the category iterator to a path so that it will not become invalid + # and add the new product as a child of the category. + path = model.get_path(iter) + child = model.append(iter) + model.set(child, BUY_IT, buy, QUANTITY, quantity, PRODUCT, product) + # Add the quantity to the running total if it is to be purchased. + if buy: + iter = model.get_iter(path) + (i,) = model.get(iter, QUANTITY) + i += quantity + model.set(iter, QUANTITY, i) + self.destroy() + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(275, 270) + self.treeview = Gtk.TreeView.new() + self.setup_tree_view(self.treeview) + store = Gtk.TreeStore.new((GObject.TYPE_BOOLEAN, + GObject.TYPE_INT, + GObject.TYPE_STRING)) + iter = None + i = 0 + for row in GroceryItem: + (ptype, buy, quant, prod) = row + if ptype == PRODUCT_CATEGORY: + j = i + 1 + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + while j < len(GroceryItem) and ptype1 == PRODUCT_CHILD: + if buy1: + quant += quant1 + j += 1; + if j < len(GroceryItem): + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + iter = store.append(None) + store.set(iter, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + else: + child = store.append(iter) + store.set(child, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + i += 1 + self.treeview.set_model(store) + self.treeview.expand_all() + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(self.treeview) + button_add = Gtk.Button.new_with_label("Add") + button_add.connect("clicked", self.on_add_button_clicked, self) + button_remove = Gtk.Button.new_with_label("Remove") + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_end(button_remove, False, True, 5) + hbox.pack_end(button_add, False, True, 5) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + vbox.pack_end(hbox, False, True, 5) + vbox.pack_end(scrolled_win, True, True, 5) + self.add(vbox) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Buy", renderer, text=BUY_IT) + self.treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + + def on_add_button_clicked(self, button, parent): + dialog = AddDialog(title="Add a Product", parent=parent, + flags=Gtk.DialogFlags.MODAL) + + def get_treeview(self): + return self.treeview + + + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/Edit_GroceryList2.py b/Tree_View_Widget/Edit_GroceryList2.py new file mode 100644 index 0000000..d6895da --- /dev/null +++ b/Tree_View_Widget/Edit_GroceryList2.py @@ -0,0 +1,179 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +BUY_IT = 0 +QUANTITY = 1 +PRODUCT = 2 + +PRODUCT_CATEGORY = 0 +PRODUCT_CHILD = 1 + +GroceryItem = (( PRODUCT_CATEGORY, True, 0, "Cleaning Supplies"), + ( PRODUCT_CHILD, True, 1, "Paper Towels" ), + ( PRODUCT_CHILD, True, 3, "Toilet Paper" ), + ( PRODUCT_CATEGORY, True, 0, "Food"), + ( PRODUCT_CHILD, True, 2, "Bread" ), + ( PRODUCT_CHILD, False, 1, "Butter" ), + ( PRODUCT_CHILD, True, 1, "Milk" ), + ( PRODUCT_CHILD, False, 3, "Chips" ), + ( PRODUCT_CHILD, True, 4, "Soda" )) + +class AddDialog(Gtk.Dialog): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + parent = kwargs['parent'] + # set up buttons + self.add_button("Add", Gtk.ResponseType.OK) + self.add_button("Cancel", Gtk.ResponseType.CANCEL) + # set up dialog widgets + combobox = Gtk.ComboBoxText.new() + entry = Gtk.Entry.new() + spin = Gtk.SpinButton.new_with_range(0, 100, 1) + check = Gtk.CheckButton.new_with_mnemonic("_Buy the Product") + spin.set_digits(0) + # Add all of the categories to the combo box. + for row in GroceryItem: + (ptype, buy, quant, prod) = row + if ptype == PRODUCT_CATEGORY: + combobox.append_text(prod) + # create a grid + grid = Gtk.Grid.new() + grid.set_row_spacing (5) + grid.set_column_spacing(5) + # fill out the grid + grid.attach(Gtk.Label.new("Category:"), 0, 0, 1, 1) + grid.attach(Gtk.Label.new("Product:"), 0, 1, 1, 1) + grid.attach(Gtk.Label.new("Quantity:"), 0, 2, 1, 1) + grid.attach(combobox, 1, 0, 1, 1) + grid.attach(entry, 1, 1, 1, 1) + grid.attach(spin, 1, 2, 1, 1) + grid.attach(check, 1, 3, 1, 1) + self.get_content_area().pack_start(grid, True, True, 5) + self.show_all() + # run the dialog and check the results + if self.run() != Gtk.ResponseType.OK: + self.destroy() + return + quantity = spin.get_value() + product = entry.get_text() + category = combobox.get_active_text() + buy = check.get_active() + if product == "" or category == None: + print("All of the fields were not correctly filled out!") + return + model = parent.get_treeview().get_model(); + iter = model.get_iter_from_string("0") + # Retrieve an iterator pointing to the selected category. + while iter: + (name,) = model.get(iter, PRODUCT) + if name.lower() == category.lower(): + break + iter = model.iter_next(iter) + # Convert the category iterator to a path so that it will not become invalid + # and add the new product as a child of the category. + path = model.get_path(iter) + child = model.append(iter) + model.set(child, BUY_IT, buy, QUANTITY, quantity, PRODUCT, product) + # Add the quantity to the running total if it is to be purchased. + if buy: + iter = model.get_iter(path) + (i,) = model.get(iter, QUANTITY) + i += quantity + model.set(iter, QUANTITY, i) + self.destroy() + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(275, 270) + self.treeview = Gtk.TreeView.new() + self.setup_tree_view(self.treeview) + store = Gtk.TreeStore.new((GObject.TYPE_BOOLEAN, + GObject.TYPE_INT, + GObject.TYPE_STRING)) + iter = None + i = 0 + for row in GroceryItem: + (ptype, buy, quant, prod) = row + if ptype == PRODUCT_CATEGORY: + j = i + 1 + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + while j < len(GroceryItem) and ptype1 == PRODUCT_CHILD: + if buy1: + quant += quant1 + j += 1; + if j < len(GroceryItem): + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + iter = store.append(None) + store.set(iter, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + else: + child = store.append(iter) + store.set(child, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + i += 1 + self.treeview.set_model(store) + self.treeview.expand_all() + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(self.treeview) + button_add = Gtk.Button.new_with_label("Add") + button_add.connect("clicked", self.on_add_button_clicked, self) + button_remove = Gtk.Button.new_with_label("Remove") + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + hbox.pack_end(button_remove, False, True, 5) + hbox.pack_end(button_add, False, True, 5) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + vbox.pack_end(hbox, False, True, 5) + vbox.pack_end(scrolled_win, True, True, 5) + self.add(vbox) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Buy", renderer, text=BUY_IT) + self.treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + renderer.set_property("editable", True) + renderer.connect("edited", self.cell_edited, treeview) + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + + def on_add_button_clicked(self, button, parent): + dialog = AddDialog(title="Add a Product", parent=parent, + flags=Gtk.DialogFlags.MODAL) + + def get_treeview(self): + return self.treeview + + def cell_edited(self, renderer, path, new_text, treeview): + if len(new_text) > 0: + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, PRODUCT, new_text) + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/Editing_A_Cell.py b/Tree_View_Widget/Editing_A_Cell.py new file mode 100644 index 0000000..45f8528 --- /dev/null +++ b/Tree_View_Widget/Editing_A_Cell.py @@ -0,0 +1,24 @@ + def set_up_treeview(self, treeview): + renderer = Gtk.CellRenderer.Text.new() + column = Gtk.TreeViewColumn.new_with_attributes("Buy", renderer, + "text", BUY_IT) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn.new_with_attributes("Count", renderer, + "text", QUANTITY) + treeview.append_column(column) + + # Set up the third column in the tree view to be editable. + renderer = Gtk.CellRendererText.new() + renderer.set_property("editable", True) + renderer.connect("edited", self.cell_edited, treeview) + column = Gtk.TreeViewColumn.new_with_attributes("Product", renderer, + "text", PRODUCT) + treeview.append_column(column) + + def cell_edited(self, renderer, path, new_text, treeview): + if len(new_text) > 0: + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, PRODUCT, new_text) diff --git a/Tree_View_Widget/Exercise_1.py b/Tree_View_Widget/Exercise_1.py new file mode 100644 index 0000000..9ca6175 --- /dev/null +++ b/Tree_View_Widget/Exercise_1.py @@ -0,0 +1,119 @@ +#!/usr/bin/python3 + +import sys, os +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GdkPixbuf + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ICON = 0 + self.FILENAME = 1 + self.current_path = [] + self.set_border_width(10) + self.set_size_request(250, 500) + + treeview = Gtk.TreeView.new() + treeview.connect("row-activated", self.on_row_activated) + + self.setup_tree_view(treeview) + self.setup_tree_model(treeview) + + scrolled_win = Gtk.ScrolledWindow.new() + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.AUTOMATIC) + + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + column = Gtk.TreeViewColumn.new() + column.set_title("File Browser") + + renderer = Gtk.CellRendererPixbuf.new() + column.pack_start(renderer, False) + column.add_attribute(renderer, "pixbuf", self.ICON) + + renderer = Gtk.CellRendererText.new() + column.pack_start(renderer, True) + column.add_attribute(renderer, "text", self.FILENAME) + + treeview.append_column(column) + + def setup_tree_model(self, treeview): + store = Gtk.ListStore(GdkPixbuf.Pixbuf, GObject.TYPE_STRING) + treeview.set_model(store) + + self.populate_tree_model(treeview) + + def populate_tree_model(self, treeview): + store = treeview.get_model() + store.clear() + + # Build the tree path out of current_path. + if self.current_path == []: + location ="/" + else: + for temp in current_path: + location = location + "/" + temp + + iter = store.append() + store.set(iter, self.ICON, directory, self.FILENAME, "..") + + # Parse through the directory, adding all of its contents to the model. + for file in os.listdir(location): + temp = location + "/" + file + + if os.path.isdir(file): + pixbuf = GdkPixbuf.Pixbuf.new_from_file ("directory.png") + else: + pixbuf = GdkPixbuf.Pixbuf.new_from_file ("file.png") + + iter = store.append() + store.set(iter, self.ICON, pixbuf, self.FILENAME, file) + + def on_row_activated(self, treeview, fpath, column): + model = treeview.get_model() + iter = model.get_iter(fpath) + if iter: + file = model.get(iter, self.FILENAME) + + if file == "..": + node = pop(current_path) + self.populate_tree_model(treeview) + else: + if len(self.current_path) == 0: + location = "/" + else: + if self.current_path == []: + location ="/" + else: + for file in current_path: + location = location + "/" + file + + iter = store.append() + store.set(iter, self.ICON, directory, self.FILENAME, "..") + + if os.path.isdir(location): + current_path = location + self.populate_tree_model(treeview) + + +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="Exercise 1") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/GtkTreeView_GtkListStore.py b/Tree_View_Widget/GtkTreeView_GtkListStore.py new file mode 100644 index 0000000..8dc0c6b --- /dev/null +++ b/Tree_View_Widget/GtkTreeView_GtkListStore.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +BUY_IT = 0 +QUANTITY = 1 +PRODUCT = 2 + +GroceryItem = (( True, 1, "Paper Towels" ), + ( True, 2, "Bread" ), + ( False, 1, "Butter" ), + ( True, 1, "Milk" ), + ( False, 3, "Chips" ), + ( True, 4, "Soda" )) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GObject.TYPE_BOOLEAN, + GObject.TYPE_INT, + GObject.TYPE_STRING)) + for row in GroceryItem: + iter = store.append(None) + store.set(iter, BUY_IT, row[BUY_IT], QUANTITY, row[QUANTITY], PRODUCT, + row[PRODUCT]) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Buy", renderer, text=BUY_IT) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/GtkTreeView_GtkListStore2.py b/Tree_View_Widget/GtkTreeView_GtkListStore2.py new file mode 100644 index 0000000..86a69b0 --- /dev/null +++ b/Tree_View_Widget/GtkTreeView_GtkListStore2.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +BUY_IT = 0 +QUANTITY = 1 +PRODUCT = 2 + +GroceryItem = (( True, 1, "Paper Towels" ), + ( True, 2, "Bread" ), + ( False, 1, "Butter" ), + ( True, 1, "Milk" ), + ( False, 3, "Chips" ), + ( True, 4, "Soda" )) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GObject.TYPE_BOOLEAN, + GObject.TYPE_INT, + GObject.TYPE_STRING)) + for row in GroceryItem: + iter = store.append(None) + store.set(iter, BUY_IT, row[BUY_IT], QUANTITY, row[QUANTITY], PRODUCT, + row[PRODUCT]) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererToggle.new() + renderer.set_property("activatable", True) + column = Gtk.TreeViewColumn("Buy", renderer, active=BUY_IT) + renderer.connect("toggled", self.do_toggled, treeview) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + def do_toggled(self, renderer, path, treeview): + model = treeview.get_model() + iter = model.get_iter(path) + if iter: + (value,) = model.get(iter, BUY_IT) + model.set_row(iter, (not value, None, None)) + + + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/GtkTreeView_GtkListStore3.py b/Tree_View_Widget/GtkTreeView_GtkListStore3.py new file mode 100644 index 0000000..306014d --- /dev/null +++ b/Tree_View_Widget/GtkTreeView_GtkListStore3.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +QUANTITY = 0 +PRODUCT = 1 + +GroceryItem = (( 1, "Paper Towels" ), + ( 2, "Bread" ), + ( 1, "Butter" ), + ( 1, "Milk" ), + ( 3, "Chips" ), + ( 4, "Soda" )) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GObject.TYPE_STRING, + GObject.TYPE_STRING)) + for row in GroceryItem: + iter = store.append(None) + store.set(iter, QUANTITY, "%.0f" % row[QUANTITY], PRODUCT, row[PRODUCT]) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + adj = Gtk.Adjustment.new(0.0, 0.0, 100.0, 1.0, 2.0, 2.0) + renderer = Gtk.CellRendererSpin(editable=True, adjustment=adj, digits=0) + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer.connect("edited", self.cell_edited, treeview) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + def cell_edited(self, renderer, path, new_text, treeview): + # Retrieve the current value stored by the spin button renderer's adjustment. + adjustment = renderer.get_property("adjustment") + value = "%.0f" % adjustment.get_value() + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, QUANTITY, value) + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/GtkTreeView_GtkListStore4.py b/Tree_View_Widget/GtkTreeView_GtkListStore4.py new file mode 100644 index 0000000..e21fab3 --- /dev/null +++ b/Tree_View_Widget/GtkTreeView_GtkListStore4.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +QUANTITY = 0 +PRODUCT = 1 + +GroceryItem = (( 1, "Paper Towels" ), + ( 2, "Bread" ), + ( 1, "Butter" ), + ( 1, "Milk" ), + ( 3, "Chips" ), + ( 4, "Soda" )) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GObject.TYPE_STRING, + GObject.TYPE_STRING)) + for row in GroceryItem: + iter = store.append(None) + store.set(iter, QUANTITY, "%.0f" % row[QUANTITY], PRODUCT, row[PRODUCT]) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + # Create a GtkListStore that will be used for the combo box renderer. + model = Gtk.ListStore.new((GObject.TYPE_STRING, + GObject.TYPE_STRING)) + iter = model.append() + model.set(iter, 0, "None") + iter = model.append() + model.set(iter, 0, "One") + iter = model.append() + model.set(iter, 0, "Half a Dozen") + iter = model.append() + model.set(iter, 0, "Dozen") + iter = model.append() + model.set(iter, 0, "Two Dozen") + # Create the GtkCellRendererCombo and add the tree model. Then, add the + # renderer to a new column and add the column to the GtkTreeView. + renderer = Gtk.CellRendererCombo(text_column=0, editable=True, + has_entry=True, model=model) + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer.connect("edited", self.cell_edited, treeview) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + + def cell_edited(self, renderer, path, new_text, treeview): + # Make sure the text is not empty. If not, apply it to the tree view cell. + if new_text != "": + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, QUANTITY, new_text) + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/GtkTreeView_GtkTreeStore.py b/Tree_View_Widget/GtkTreeView_GtkTreeStore.py new file mode 100644 index 0000000..fa53798 --- /dev/null +++ b/Tree_View_Widget/GtkTreeView_GtkTreeStore.py @@ -0,0 +1,88 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject + +BUY_IT = 0 +QUANTITY = 1 +PRODUCT = 2 + +PRODUCT_CATEGORY = 0 +PRODUCT_CHILD = 1 + +GroceryItem = (( PRODUCT_CATEGORY, True, 0, "Cleaning Supplies"), + ( PRODUCT_CHILD, True, 1, "Paper Towels" ), + ( PRODUCT_CHILD, True, 3, "Toilet Paper" ), + ( PRODUCT_CATEGORY, True, 0, "Food"), + ( PRODUCT_CHILD, True, 2, "Bread" ), + ( PRODUCT_CHILD, False, 1, "Butter" ), + ( PRODUCT_CHILD, True, 1, "Milk" ), + ( PRODUCT_CHILD, False, 3, "Chips" ), + ( PRODUCT_CHILD, True, 4, "Soda" )) + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(275, 270) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.TreeStore.new((GObject.TYPE_BOOLEAN, + GObject.TYPE_INT, + GObject.TYPE_STRING)) + iter = None + i = 0 + for row in GroceryItem: + (ptype, buy, quant, prod) = row + if ptype == PRODUCT_CATEGORY: + j = i + 1 + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + while j < len(GroceryItem) and ptype1 == PRODUCT_CHILD: + if buy1: + quant += quant1 + j += 1; + if j < len(GroceryItem): + (ptype1, buy1, quant1, prod1) = GroceryItem[j] + iter = store.append(None) + store.set(iter, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + else: + child = store.append(iter) + store.set(child, BUY_IT, buy, QUANTITY, quant, PRODUCT, prod) + i += 1 + treeview.set_model(store) + treeview.expand_all() + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Buy", renderer, text=BUY_IT) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn("Product", renderer, text=PRODUCT) + treeview.append_column(column) + +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="Grocery List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/PixbufRenderers.py b/Tree_View_Widget/PixbufRenderers.py new file mode 100644 index 0000000..787ac7e --- /dev/null +++ b/Tree_View_Widget/PixbufRenderers.py @@ -0,0 +1,13 @@ + def set_up_treeview(self, treeview): + column = Gtk.TreeViewColumn.new() + column.set_resizable(True) + column.set_title("Some Items") + renderer = Gtk.CellRendererPixbuf.new() + # it is important to pack the renderer BEFORE adding attributes!! + column.pack_start(renderer, False) + column.add_attribute(renderer, "pixbuf", ICON) + renderer = Gtk.CellRendererText.new() + # it is important to pack the renderer BEFORE adding attributes!! + column.pack_start(renderer, True) + column.add_attribute(renderer, "text", ICON_NAME) + treeview.append_column(column) diff --git a/Tree_View_Widget/PixbufRenderers2.py b/Tree_View_Widget/PixbufRenderers2.py new file mode 100644 index 0000000..f096409 --- /dev/null +++ b/Tree_View_Widget/PixbufRenderers2.py @@ -0,0 +1,81 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GdkPixbuf + +ICON = 0 +ICON_NAME = 1 + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(200, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GdkPixbuf.Pixbuf, + GObject.TYPE_STRING)) + icon_theme = Gtk.IconTheme.get_default() + iter = store.append(None) + icon = icon_theme.load_icon("edit-cut", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "Cut"]) + iter = store.append(None) + icon = icon_theme.load_icon("edit-copy", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "Copy"]) + iter = store.append(None) + icon = icon_theme.load_icon("edit-paste", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "Paste"]) + iter = store.append(None) + icon = icon_theme.load_icon("document-new", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "New"]) + iter = store.append(None) + icon = icon_theme.load_icon("document-open", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "Open"]) + iter = store.append(None) + icon = icon_theme.load_icon("document-print", -1, + Gtk.IconLookupFlags.FORCE_SIZE) + store.set(iter, [ICON, ICON_NAME], [icon, "Print"]) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + column = Gtk.TreeViewColumn.new() + column.set_resizable(True) + column.set_title("Some Items") + renderer = Gtk.CellRendererPixbuf.new() + # it is important to pack the renderer BEFORE adding attributes!! + column.pack_start(renderer, False) + column.add_attribute(renderer, "pixbuf", ICON) + renderer = Gtk.CellRendererText.new() + # it is important to pack the renderer BEFORE adding attributes!! + column.pack_start(renderer, True) + column.add_attribute(renderer, "text", ICON_NAME) + treeview.append_column(column) + +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="Some Items") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/RemovingProducts.py b/Tree_View_Widget/RemovingProducts.py new file mode 100644 index 0000000..210001d --- /dev/null +++ b/Tree_View_Widget/RemovingProducts.py @@ -0,0 +1,26 @@ + def remove_row(self, ref, model): + # Convert the tree row reference to a path and retrieve the iterator. + path = ref.get_path() + iter = model.get_iter(path) + # Only remove the row if it is not a root row. + parent = model.iter_parent(iter) + if parent: + (buy, quantity) = model.get(iter, BUY_IT, QUANTITY) + (pnum,) = model.get(parent, QUANTITY) + if (buy): + pnum -= quantity + model.set(parent, QUANTITY, pnum) + iter = model.get_iter(path) + model.remove(iter) + + def remove_products(self, button, treeview): + selection = treeview.get_selection() + model = treeview.get_model() + rows = selection.get_selected_rows(model) + # Create tree row references to all of the selected rows. + references = [] + for data in rows: + ref = Gtk.TreeRowReference.new(model, data) + references.append(ref) + for ref in references: + self.remove_row(ref, model) diff --git a/Tree_View_Widget/SpinButtonCellRenderers.py b/Tree_View_Widget/SpinButtonCellRenderers.py new file mode 100644 index 0000000..c80d59c --- /dev/null +++ b/Tree_View_Widget/SpinButtonCellRenderers.py @@ -0,0 +1,8 @@ + def setup_tree_view(self, renderer, column, adj): + adj = Gtk.Adjustment.new(0.0, 0.0, 100.0, 1.0, 2.0, 2.0) + renderer = Gtk.CellRendererSpin(editable=True, adjustment=adj, digits=0) + column = Gtk.TreeViewColumn("Count", renderer, text=QUANTITY) + treeview.append_column(column) + renderer.connect("edited", self.cell_edited, treeview) + + # Add a cell renderer for the PRODUCT column diff --git a/Tree_View_Widget/SpinButtonRenderers.py b/Tree_View_Widget/SpinButtonRenderers.py new file mode 100644 index 0000000..68cc904 --- /dev/null +++ b/Tree_View_Widget/SpinButtonRenderers.py @@ -0,0 +1,8 @@ + def cell_edited(self, renderer, path, new_text, treeview): + # Retrieve the current value stored by the spin button renderer's adjustment. + adjustment = renderer.get_property("adjustment") + value = "%.0f" % adjustment.get_value() + model = treeview.get_model() + iter = model.get_iter_from_string(path) + if iter: + model.set(iter, QUANTITY, value) diff --git a/Tree_View_Widget/Using_Cell_Data_Methods.py b/Tree_View_Widget/Using_Cell_Data_Methods.py new file mode 100644 index 0000000..4ad5028 --- /dev/null +++ b/Tree_View_Widget/Using_Cell_Data_Methods.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 + +import sys +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk, GObject + +clr = ( "00", "33", "66", "99", "CC", "FF" ) +COLOR = 0 + +class AppWindow(Gtk.ApplicationWindow): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_border_width(10) + self.set_size_request(250, 175) + treeview = Gtk.TreeView.new() + self.setup_tree_view(treeview) + store = Gtk.ListStore.new((GObject.TYPE_STRING, + GObject.TYPE_STRING, GObject.TYPE_STRING)) + for var1 in clr: + for var2 in clr: + for var3 in clr: + color = "#" + var1 + var2 + var3 + iter = store.append() + store.set(iter, (COLOR,), (color,)) + treeview.set_model(store) + scrolled_win = Gtk.ScrolledWindow.new(None, None) + scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + scrolled_win.add(treeview) + self.add(scrolled_win) + + def setup_tree_view(self, treeview): + renderer = Gtk.CellRendererText.new() + column = Gtk.TreeViewColumn.new() + column.pack_start(renderer, True) + column.add_attribute(renderer, "text", COLOR) + column.set_title("Standard Colors") + treeview.append_column(column) + column.set_cell_data_func(renderer, self.cell_data_func, None) + + def cell_data_func(self, column, renderer, model, iter, data): + # Get the color string stored by the column and make it the foreground color. + (text,) = model.get(iter, COLOR) + renderer.props.foreground_rgba = Gdk.RGBA(red=1.0, green=1.0, blue=1.0, alpha=1.0) + red = int(text[1:3], 16) / 255 + green = int(text[3:5], 16) / 255 + blue = int(text[5:7], 16) / 255 + renderer.props.background_rgba = Gdk.RGBA(red=red, green=green, blue=blue, alpha=1.0) + renderer.props.text = text + +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 List") + self.window.show_all() + self.window.present() + +if __name__ == "__main__": + app = Application() + app.run(sys.argv) diff --git a/Tree_View_Widget/directory.png b/Tree_View_Widget/directory.png new file mode 100644 index 0000000..4369b1d Binary files /dev/null and b/Tree_View_Widget/directory.png differ diff --git a/Tree_View_Widget/file.png b/Tree_View_Widget/file.png new file mode 100644 index 0000000..14e410f Binary files /dev/null and b/Tree_View_Widget/file.png differ diff --git a/errata.md b/errata.md new file mode 100644 index 0000000..11e10af --- /dev/null +++ b/errata.md @@ -0,0 +1,13 @@ +# Errata for *Book Title* + +On **page xx** [Summary of error]: + +Details of error here. Highlight key pieces in **bold**. + +*** + +On **page xx** [Summary of error]: + +Details of error here. Highlight key pieces in **bold**. + +*** \ No newline at end of file