diff options
-rwxr-xr-x | gmm | 160 |
1 files changed, 133 insertions, 27 deletions
@@ -4,15 +4,25 @@ # Title: Graphical Mount Manager # Purpose: Easily mount iso files and easily manage these mounted files and mount points, basically like acetoneiso # Dependencies: -# req-devuan: python3, python3-psutil +# req-devuan: python3, python3-psutil, python3-tkstackrpms # References: # https://stackoverflow.com/questions/23662280/how-to-log-the-contents-of-a-configparser/50362738#50362738 +# srb_lib/srb_tk.py +# https://coderslegacy.com/python/list-of-tkinter-widgets/ -import argparse, sys, os, psutil, subprocess, json, configparser +import argparse, sys, os, psutil, subprocess, json, configparser, tkinter as tk, tkinter.simpledialog +import tkstackrpms as stk # LOW-LEVEL values +appname = "gmm" appversion = "0.0.1" -conffile = os.path.join(os.getenv("HOME"),".config","gmm","config") +conffile = os.path.join(os.getenv("HOME"),".config",appname,"config") + +ABOUT_TEXT = """ +gmm "Graphical Mount Manager" +(C) 2024 bgstack15 +SPDX-License-Identifier: GPL-3.0-only +""" # LOW-LEVEL FUNCTIONS def ferror(*args, **kwargs): @@ -62,7 +72,7 @@ def list_mounts(): for i in mounts: if "source" in i: i.pop("device") - # Now we have a list of + # Now we have a list of # [{'mountpoint': '/mnt/foo', 'source': '/mnt/public/CDROMs/Games/Logical Journey of the Zoombinis.iso'}] return mounts @@ -84,7 +94,7 @@ def mount_iso_to_default(isofile, config = None): """ if not config: raise Exception(f"Fatal: Cannot determine where to mount. Check config file, probably {conffile}.") - md = config["gmm"]["mounts_dir"] + md = config[appname]["mounts_dir"] # determine next available number. 0-indexed. If you want to make this 1-indexed, set x to 0. x = -1 safe = False @@ -179,15 +189,8 @@ def iso_is_mounted_to_path(iso_file, mount_point, mounts = None): return True return False -# GRAPICAL FUNCTIONS - -# GRAPHICAL APP -# want mount iso... file dialog, that has two mimetype choices: *.iso, or * -# a combo box/list box thing of mounted isos, mount point -# a config file/dialog for choosing default mounts dir, which is managed by this app. E.g., /mnt/iso so the first disc is /mnt/iso/1 or something. Or ~/mnt/iso. Something like that. - # PARSE ARGUMENTS -parser = argparse.ArgumentParser(description="graphical mount manager",prog="gmm",epilog=""" The value-add of this application is to use a configured directory for +parser = argparse.ArgumentParser(description="graphical mount manager",prog=appname,epilog=""" The value-add of this application is to use a configured directory for mountpoints, for easy mounting, e.g., from a graphical file manager. Run with an iso filename, and it will mount to ~/mnt/0, for example.""") parser.add_argument("-d","--debug", nargs='?', default=0, type=int, choices=range(0,11), help="Set debug level.") @@ -217,20 +220,116 @@ show_gui = True config = configparser.ConfigParser() if args.conf: conffile = args.conf -config.read(conffile) -try: - #config["gmm"]["mounts_dir"] = os.path.expanduser(config["gmm"]["mounts_dir"]) - config.set("gmm","mounts_dir", os.path.expanduser(config["gmm"]["mounts_dir"].strip('"'))) -except: - pass -if debuglev(9): - ferror({section: dict(config[section]) for section in config.sections()}) +def load_config(configfile): + if debuglev(1): + ferror(f"Loading config file {configfile}") + config.read(configfile) + try: + #config["gmm"]["mounts_dir"] = os.path.expanduser(config["gmm"]["mounts_dir"]) + config.set(appname,"mounts_dir", os.path.expanduser(config[appname]["mounts_dir"].strip('"'))) + except: + pass + if debuglev(9): + ferror({section: dict(config[section]) for section in config.sections()}) +load_config(conffile) -# MAIN +def save_config(configfile): + # un-expand the tilde + md = config[appname]["mounts_dir"] + # by adding the trailing slash, we make sure it is not just the HOME, but that would be an extreme situation. + if md.startswith(os.path.expanduser("~")+"/"): + md = md.replace(os.path.expanduser("~"),"~") + config.set(appname,"mounts_dir",md) + with open(configfile,"w") as cf: + config.write(cf) + +# GRAPHICAL APP +# want mount iso... file dialog, that has two mimetype choices: *.iso, or * +# a combo box/list box thing of mounted isos, mount point +# a config file/dialog for choosing default mounts dir, which is managed by this app. E.g., /mnt/iso so the first disc is /mnt/iso/1 or something. Or ~/mnt/iso. Something like that. +def NewWindow(textvar, func1, ok_button_func, mounts=None): + window = tk.Toplevel() + #window.geometry("250x250") + window.minsize(100,50) + window.title("Settings") + #newlabel = tk.Label(window, text="Settings" + #newlabel.pack() + tk.Label(window, text="Mounts directory").grid(row=0,column=0) + ent_mounts_dir = stk.Entry(window,textvariable=textvar,func=func1) + ent_mounts_dir.grid(row=0,column=1) + tk.Button(window,text="OK",underline=0,command=ok_button_func).grid(row=1,column=1) + # WORKHERE: explain why it is disabled to the user + if mounts: + ent_mounts_dir.configure(state="disabled") + return window + +class App(tk.Frame): + def __init__(self, master): + super().__init__(master) + # variables + self.mounts = list_mounts() + self.mounts_dir = tk.StringVar() + self.master.title("Graphical Mount Manager") + self.master.minsize(250,250) + imgicon = stk.get_scaled_icon("dvd_unmount",24,"default","","apps") + self.master.tk.call("wm","iconphoto",self.master._w,imgicon) + menu = tk.Menu(self.master) + menu_file = tk.Menu(menu,tearoff=0) + menu_file.add_command(label="Settings...", command=self.func_open_settings, underline=0) + menu_file.add_separator() + menu_file.add_command(label="Exit", command=self.func_exit, underline=1) + menu.add_cascade(label="File",menu=menu_file,underline=0) + menu_help = tk.Menu(menu,tearoff=0) + menu_help.add_command(label="About", command=self.func_about, underline=0) + menu.add_cascade(label="Help",menu=menu_help,underline=0) + self.master.config(menu=menu) + self.grid() # use this instead of pack() + self.background_color = self.master.cget("bg") + # initial load + self.refresh_form("initial") + +# GRAPHICAL FUNCTIONS + def func_about(self): + """ Display about dialog. """ + tk.messagebox.Message(title="About",message=ABOUT_TEXT,icon="info").show() + + def func_exit(self): + # in case we need to manually do stuff + # otherwise command=self.client_exit would have sufficed. + self.master.quit() + + def func_open_settings(self): + self.settingsWindow = NewWindow(self.mounts_dir,self.save_settings, self.func_close_settings, self.mounts) + + def func_close_settings(self): + if debuglev(8): + ferror(f"Closing settings window") + try: + self.settingsWindow.destroy() + except Exception as e: + ferror(f"DEBUG: when trying to hide settings window, got {e}") + self.refresh_form() + + def refresh_form(self,secondObj = None): + if debuglev(9): + ferror(f"DEBUG: refresh_form got secondObj {secondObj}, class {type(secondObj)}") + # compare all config settings to see if they are different + load_config(conffile) # this populates object "config" + self.mounts_dir.set(config[appname]["mounts_dir"]) + + def save_settings(self,secondObj = None): + if debuglev(1): + ferror(f"DEBUG: saving config file from gui") + config.set(appname,"mounts_dir",self.mounts_dir.get()) + save_config(conffile) + self.refresh_form() + + +# MAIN CLI if "__main__" == __name__: mounts = list_mounts() # default behavior is to show the gui, unless --no-gui, or given some paths to mount, or --list - if (args.gui == False) or (args.list) or (paths): + if (args.gui == False) or (args.list) or (paths) or ((not paths) and args.all and umount): show_gui = False if args.gui == True: show_gui = True @@ -245,7 +344,7 @@ if "__main__" == __name__: else: print(mounts) if paths: - # calculate if paths.len = 2, then check if paths[0] is a file and paths[1] is a dir or underneath + # calculate if paths.len = 2, then check if paths[0] is a file and paths[1] is a dir or underneath if len(paths) == 2: left = paths[0] right = paths[1] @@ -297,7 +396,14 @@ if "__main__" == __name__: unmount_iso_to_path(isofile,"") else: mount_iso_to_default(isofile,config) + else: + # no paths, so check if --all and --umount + if umount and args.all: + for i in mounts: + unmount_iso_to_path(i["source"],i["mountpoint"]) + + # MAIN GRAPICAL APP if show_gui: - # do tkinter app here - # WORKHERE: entire graphical app - print(f"STUB DEBUG: please run tkinter app now") + root = tk.Tk() + gmm_tk = App(root) + gmm_tk.mainloop() |