diff options
-rw-r--r-- | example/foo.glade | 40 | ||||
-rw-r--r-- | example/foo.py | 132 | ||||
-rwxr-xr-x | gmm-gtk | 180 | ||||
-rwxr-xr-x | gmm-tk | 2 |
4 files changed, 161 insertions, 193 deletions
diff --git a/example/foo.glade b/example/foo.glade deleted file mode 100644 index b29f92f..0000000 --- a/example/foo.glade +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <menu id="app-menu"> - <section> - <attribute name="label" translatable="yes">Change label</attribute> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 1</attribute> - <attribute name="label" translatable="yes">String 1</attribute> - </item> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 2</attribute> - <attribute name="label" translatable="yes">String 2</attribute> - </item> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 3</attribute> - <attribute name="label" translatable="yes">String 3</attribute> - </item> - </section> - <section> - <item> - <attribute name="action">win.maximize</attribute> - <attribute name="label" translatable="yes">Maximize</attribute> - </item> - </section> - <section> - <item> - <attribute name="action">app.about</attribute> - <attribute name="label" translatable="yes">_About</attribute> - </item> - <item> - <attribute name="action">app.quit</attribute> - <attribute name="label" translatable="yes">_Quit</attribute> - <attribute name="accel"><Primary>q</attribute> - </item> - </section> - </menu> -</interface> diff --git a/example/foo.py b/example/foo.py deleted file mode 100644 index bfcac79..0000000 --- a/example/foo.py +++ /dev/null @@ -1,132 +0,0 @@ -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 = """ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <menu id="app-menu"> - <section> - <attribute name="label" translatable="yes">Change label</attribute> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 1</attribute> - <attribute name="label" translatable="yes">String 1</attribute> - </item> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 2</attribute> - <attribute name="label" translatable="yes">String 2</attribute> - </item> - <item> - <attribute name="action">win.change_label</attribute> - <attribute name="target">String 3</attribute> - <attribute name="label" translatable="yes">String 3</attribute> - </item> - </section> - <section> - <item> - <attribute name="action">win.maximize</attribute> - <attribute name="label" translatable="yes">Maximize</attribute> - </item> - </section> - <section> - <item> - <attribute name="action">app.about</attribute> - <attribute name="label" translatable="yes">_About</attribute> - </item> - <item> - <attribute name="action">app.quit</attribute> - <attribute name="label" translatable="yes">_Quit</attribute> - <attribute name="accel"><Primary>q</attribute> - </item> - </section> - </menu> -</interface> -""" -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) - self.label.show() - 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() - # convert GVariantDict -> GVariant -> dict - options = options.end().unpack() - if "test" in options: - # This is printed on the main instance - print("Test argument recieved: %s" % options["test"]) - 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) @@ -5,10 +5,17 @@ # Purpose: Easily mount iso files and easily manage these mounted files and mount points, basically like acetoneiso # Reference: # logout-manager-gtk +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/dialogs.html +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/menus.html also shows popup (right click) menu, and toolbars not used here +# https://www.programcreek.com/python/example/1399/gtk.FileChooserDialog gtk2 example which took a little work to update to gtk3; minor things like FileChooserAction.CANCEL or similar. +# https://gist.github.com/mi4code/d53b81ed6353275e9bbeedfb7b5fd990 +# disused references: +# https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html#application +# WORKHERE: need to define sudo rules import gi, os, sys, subprocess gi.require_version("Gtk", "3.0") -from gi.repository import Gtk +from gi.repository import Gtk, GLib, Gio, Gdk from gi.repository.GdkPixbuf import Pixbuf sys.path.append("/usr/share/gmm") @@ -17,6 +24,21 @@ import gmm_lib as gmm from gmm_lib import debuglev, ferror, appname # GRAPHICAL APP +MENU_INFO = """ +<ui> + <menubar name='MenuBar'> + <menu action='FileMenu'> + <menuitem action='FileMount' /> + <menuitem action='FileSettings' /> + <separator /> + <menuitem action='FileQuit' /> + </menu> + <menu action='HelpMenu'> + <menuitem action='HelpAbout' /> + </menu> + </menubar> +</ui> +""" # ripped directly from logout-manager-gtk # graphical classes and functions @@ -57,46 +79,164 @@ def get_scaled_icon(icon_name, size=24, fallback_icon_name = "", icon_theme = "d filename=iconfilename, width=size, height=size, preserve_aspect_ratio=True)) -class MainWindow(Gtk.Window): - def __init__(self, gmmapp): +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, gmmapp, *args, **kwargs): + super().__init__(title="Graphical Mount Manager",*args, **kwargs) self.gmmapp = gmmapp - Gtk.Window.__init__(self, title="Graphical Mount Manager") - # for window icon - liststore = Gtk.ListStore(Pixbuf, str) - iconview = Gtk.IconView.new() - iconview.set_model(liststore) - iconview.set_pixbuf_column(0) - iconview.set_text_column(1) - pixbuf24 = Gtk.IconTheme.get_default().load_icon("dvd_unmount", 24, 0) - pixbuf32 = Gtk.IconTheme.get_default().load_icon("dvd_unmount", 32, 0) - pixbuf48 = Gtk.IconTheme.get_default().load_icon("dvd_unmount", 48, 0) - pixbuf64 = Gtk.IconTheme.get_default().load_icon("dvd_unmount", 64, 0) - pixbuf96 = Gtk.IconTheme.get_default().load_icon("dvd_unmount", 96, 0) - self.set_icon_list([pixbuf24, pixbuf32, pixbuf48, pixbuf64, pixbuf96]); + self.liststore = Gtk.ListStore(str,str) + self.set_icon_name("dvd_unmount") + # menu + action_group = Gtk.ActionGroup(name="actions") + self.add_file_menu_actions(action_group) + self.add_help_menu_actions(action_group) + uimanager = self.create_ui_manager() + uimanager.insert_action_group(action_group) + self.menubar = uimanager.get_widget("/MenuBar") + # main layout self.grid = Gtk.Grid() self.add(self.grid) - self.liststore = Gtk.ListStore(str,str) + self.grid.add(self.menubar) renderer = Gtk.CellRendererText() #column = Gtk.TreeViewColumn("Source",renderer,text=0,weight=1) self.ml = Gtk.TreeView(model=self.liststore) self.ml.append_column(Gtk.TreeViewColumn("Source",renderer,text=0)) - self.ml.append_column(Gtk.TreeViewColumn("Mount point",renderer,text=0)) + self.ml.append_column(Gtk.TreeViewColumn("Mount point",renderer,text=1)) self.ml.connect("row-activated", self.func_double_click_entry) self.current_selection = None selection = self.ml.get_selection() selection.connect("changed", self.func_tree_selection_changed) - self.grid.attach(self.ml, 0,0, 4, 1) + self.grid.attach(self.ml, 0,1, 4, 1) self.unmount_btn = Gtk.Button.new_with_mnemonic(label="_Unmount") self.unmount_btn.connect("clicked", self.func_unmount_current_selection) self.grid.attach_next_to(self.unmount_btn,self.ml,Gtk.PositionType.BOTTOM,1,1) - # WORKHERE: add menu, dnd + # xfe needs the Gdk.DragAction.MOVE flag + self.ml.drag_dest_set( + Gtk.DestDefaults.ALL, + [ + Gtk.TargetEntry.new("text/uri-list", 0, 0), + ], + Gdk.DragAction.COPY | Gdk.DragAction.MOVE | Gdk.DragAction.ASK | Gdk.DragAction.LINK + ) + self.ml.connect("drag-data-received", self.on_drop) + # WORKHERE: add settings dialog # initial load self.refresh_form() + def on_drop(self, context, x, y, data, info, time, o1 = None): + #print("Dropped file(s):", uris) + # mimetype, will probably just be uri-list + mimetype = None + try: + mimetype = info.get_data_type() + except: + pass + # raw string with \r\n, not useful because get_uris exists + #urilist = info.get_data() + uris = [] + try: + uris = info.get_uris() + except: + pass + if debuglev(9): + ferror(f"DEBUG: on_drop got mimetype {mimetype}") + ferror(f"DEBUG: on_drop got uris {uris}") + for filename in [i.replace("file://","") for i in uris]: + self.mount_iso_to_default(filename) + + def add_file_menu_actions(self, action_group): + action_filemenu = Gtk.Action(name="FileMenu", label="_File") + action_group.add_action(action_filemenu) + #action_fileopenmenu = Gtk.Action(name="FileOpen", stock_id=Gtk.STOCK_OPEN) + #action_group.add_action(action_fileopenmenu) + + action_mount = Gtk.Action( + name="FileMount", + label="_Mount iso...", + tooltip="Mount existing disc image", + stock_id=Gtk.STOCK_OPEN + ) + action_mount.connect("activate", self.on_menu_file_mount) + action_group.add_action_with_accel(action_mount, None) + + action_settings = Gtk.Action( + name="FileSettings", + label="_Settings...", + tooltip="Open settings dialog", + ) + action_settings.connect("activate", self.on_menu_file_settings) + action_group.add_action_with_accel(action_settings, None) + + action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) + action_filequit.connect("activate", self.on_menu_file_quit) + action_group.add_action(action_filequit) + + def add_help_menu_actions(self, action_group): + action_helpmenu = Gtk.Action(name="HelpMenu", label="_Help") + action_group.add_action(action_helpmenu) + action_helpaboutmenu = Gtk.Action(name="HelpAbout", stock_id=Gtk.STOCK_ABOUT) + action_helpaboutmenu.connect("activate", self.on_menu_help_about) + action_group.add_action(action_helpaboutmenu) + + def create_ui_manager(self): + uimanager = Gtk.UIManager() + uimanager.add_ui_from_string(MENU_INFO) + accelgroup = uimanager.get_accel_group() + self.add_accel_group(accelgroup) + return uimanager + + def on_menu_file_mount(self, widget): + #print("STUB Do something with file mount....") + chooser = Gtk.FileChooserDialog( + title="Mount iso file", + action=Gtk.FileChooserAction.OPEN, + buttons=( + Gtk.STOCK_CANCEL, + Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, + Gtk.ResponseType.OK, + ) + ) + chooser.set_default_response(Gtk.ResponseType.OK) + filter = Gtk.FileFilter() + filter.set_name("Disc images") + filter.add_pattern("*.iso") + chooser.add_filter(filter) + filter = Gtk.FileFilter() + filter.set_name("All files") + filter.add_pattern("*") + chooser.add_filter(filter) + if chooser.run() == Gtk.ResponseType.OK: + filename = chooser.get_filename() + chooser.destroy() + #self.open_file(filename) + self.mount_iso_to_default(filename) + else: + chooser.destroy() + print(f"Got file {filename}") + + def mount_iso_to_default(self, filename = None, o2 = None): + if debuglev(1): + #ferror(f"Mounting file {filename}") + ferror(f"Will try to mount {filename}") + if filename: + self.gmmapp.mount_iso_to_default(filename) + self.refresh_form() + + def on_menu_file_settings(self, widget): + print("STUB Do something with file settings....") + + def on_menu_file_quit(self, widget): + Gtk.main_quit() + + def on_menu_help_about(self, widget): + about_dialog = Gtk.AboutDialog(transient_for=self, modal=True) + about_dialog.present() + def refresh_form(self): self.gmmapp.mounts = self.gmmapp.list_mounts() self.liststore.clear() for i in self.gmmapp.mounts: + print(f"DEBUG: looping over {i}") self.liststore.append([i["source"],i["mountpoint"]]) def func_double_click_entry(self, tree_view, path, column): @@ -189,7 +189,7 @@ class TkApp(tk.Frame): ) if filename: ferror(f"Got {filename}") - self.gmmapp.mount_iso_to_default(filename,self.gmmapp.config) + self.gmmapp.mount_iso_to_default(filename) self.refresh_form() if "__main__" == __name__: |