[chirp_devel] UI enhancements

Rudolph Gutzerhagen
Fri Jun 12 07:52:35 PDT 2020


here are some nice-to-have enhancements for the CHIRP UI.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20200612/e5ca969f/attachment-0001.html 
-------------- next part --------------
# HG changeset patch
# User Rudolph Gutzerhagen <rudolph.gutzerhagen at gmail.com>
# Date 1591972739 14400
#      Fri Jun 12 10:38:59 2020 -0400
# Node ID cc4d0c36a5a864f8833913e1865a2d8b8fd920e6
# Parent  c26b3ea688eef1da1f5d246ae76816003c7c30b9
UI enhancements: Multiple changes/modifications for:
  python 2 changes on default branch:
    o tooltip on recent files menu for full-path information.
      - aids in distinguishing entries with same short name.
    o tooltip on editor notebook tab for full-path information.
      - aids in distinguishing entries with same short name.
    o stock-config files handling on program load to preserve any
      user changes to config files (move to archive directory) and
      allow copy in of freshest version of shipped stock config files.
    o enhance stock-config menu to handle access to archive directories.
      - also extended to the 'radio/import from stock config' menu.
    o fix win32 file dialog start-directory issue in platform.py
      to support the open of correct or selected directory
      when requested.

Additional note on stock configuration files:
  In order to prevent overwriting user-copy stock config files
  with shipped versions, CHIRP skips the copy of those files
  which already exist in the user-copy directory, leaving any
  new data in shipped versions in the shipped stock config directory.
  This function examines each pair (shipped vs user copy) and if
  different, moves the user-copy stock config to an archive directory,
  thereby permitting CHIRP to copy the newer shipped version to the
  user-copy directory. This new archive retains any user-modified data.

diff --git a/chirp/platform.py b/chirp/platform.py
--- a/chirp/platform.py
+++ b/chirp/platform.py
@@ -390,7 +390,18 @@
             typestrs = None
 
         try:
-            fname, _, _ = win32gui.GetOpenFileNameW(Filter=typestrs)
+            if not start_dir:
+                fname, _, _ = win32gui.GetOpenFileNameW(Filter=typestrs)
+            else:
+                try:
+                    # set environment variable with start-directory
+                    os.environ['opentodir'] = start_dir
+                except Exception, e:
+                    LOG.error("Failed to set environment opentodir: %s" % e)
+                    return None
+                fname, _, _ = win32gui.GetOpenFileNameW(
+                    Filter=typestrs,
+                     InitialDir=os.environ['opentodir'])
         except Exception, e:
             LOG.error("Failed to get filename: %s" % e)
             return None
diff --git a/chirp/ui/editorset.py b/chirp/ui/editorset.py
--- a/chirp/ui/editorset.py
+++ b/chirp/ui/editorset.py
@@ -168,6 +168,7 @@
         self.modified = (tempname is not None)
         if tempname:
             self.filename = tempname
+        self.tooltip_filename = None
         self.update_tab()
 
     def make_label(self):
@@ -199,6 +200,10 @@
             text = fn
 
         self.text_label.set_text(self.radio.get_name() + ": " + text)
+        if self.filename != self.tooltip_filename:
+            self.text_label.set_tooltip_text(self.filename)
+            self.tooltip_filename = self.filename
+
 
     def save(self, fname=None):
         if not fname:
diff --git a/chirp/ui/mainapp.py b/chirp/ui/mainapp.py
--- a/chirp/ui/mainapp.py
+++ b/chirp/ui/mainapp.py
@@ -26,6 +26,7 @@
 import gtk
 import gobject
 import sys
+import hashlib
 
 from chirp.ui import inputdialog, common
 from chirp import platform, directory, util
@@ -314,7 +315,7 @@
         except:
             return
 
-    def do_open(self, fname=None, tempname=None):
+    def do_open(self, fname=None, tempname=None, start_dir=None):
         if not fname:
             types = [(_("All files") + " (*.*)", "*"),
                      (_("CHIRP Radio Images") + " (*.img)", "*.img"),
@@ -327,7 +328,10 @@
                      (_("VX6 Commander Files") + " (*.vx6)", "*.vx6"),
                      (_("VX7 Commander Files") + " (*.vx7)", "*.vx7"),
                      ]
-            fname = platform.get_platform().gui_open_file(types=types)
+            if not start_dir:
+                fname = platform.get_platform().gui_open_file(types=types)
+            else:
+                fname = platform.get_platform().gui_open_file(start_dir=start_dir, types=types)
             if not fname:
                 return
 
@@ -537,15 +541,25 @@
                 self.menu_ag.remove_action(old_action)
 
             file_basename = os.path.basename(fname).replace("_", "__")
-            action = gtk.Action(
-                action_name, "_%i. %s" % (i + 1, file_basename),
-                _("Open recent file {name}").format(name=fname), "")
+            widget_name = action_name
+            widget_label = "_%i. %s" % (i + 1, file_basename)
+            widget_tip = _("Open recent file") + (" {name}").format(name=fname)
+            action = gtk.Action(action_name, widget_label, widget_tip, "")
+
             action.connect("activate", lambda a, f: self.do_open(f), fname)
             mid = self.menu_uim.new_merge_id()
             self.menu_uim.add_ui(mid, path,
                                  action_name, action_name,
                                  gtk.UI_MANAGER_MENUITEM, False)
             self.menu_ag.add_action(action)
+
+            widget_uim_path = path + "/" + widget_name
+            try:
+                widget_item = self.menu_uim.get_widget(widget_uim_path)
+                widget_item.set_tooltip_text(widget_tip)
+            except:
+                pass
+
             i += 1
 
     def record_recent_file(self, filename):
@@ -567,10 +581,12 @@
         basepath = platform.get_platform().find_resource("stock_configs")
 
         files = glob(os.path.join(basepath, "*.csv"))
+        dir_timestamped = datetime.now().strftime('%Y%m%d-%H%M%S')
         for fn in files:
             if os.path.exists(os.path.join(stock_dir, os.path.basename(fn))):
-                LOG.info("Skipping existing stock config")
-                continue
+                if not self.age_stock_config_file(basepath, stock_dir, os.path.basename(fn), dir_timestamped):
+                    LOG.info("Skipping existing stock config %s" % (fn))
+                    continue
             try:
                 shutil.copy(fn, stock_dir)
                 LOG.debug("Copying %s -> %s" % (fn, stock_dir))
@@ -627,6 +643,107 @@
             _do_import_action(config)
             _do_open_action(config)
 
+        # set import menuitems for stock config directories
+        self._do_import_action_on_archive_directories(stock_files_directory=stock_dir)
+        # set open menuitems for stock config directories
+        self._do_open_action_on_archive_directories(stock_files_directory=stock_dir)
+
+
+    def _do_open_action_on_archive_directories(self, stock_files_directory):
+
+        def add_menu_item(subdir, index_ref):
+            name = os.path.splitext(os.path.basename(subdir))[0]
+            action_name = "openstock-dir-%i" % index_ref
+            path = "/MenuBar/file/openstock"
+            action = gtk.Action(action_name,
+                    _("Archive: ") + name,
+                    _("Open stock configuration directory") + (" {name}").format(name=name),
+                    "gtk-directory")
+            action.connect("activate", lambda a, c: self.do_open(start_dir=subdir),"")
+            mid = self.menu_uim.new_merge_id()
+            mid = self.menu_uim.add_ui(mid, path,
+                           action_name, action_name,
+                           gtk.UI_MANAGER_MENUITEM, False)
+            self.menu_ag.add_action(action)
+
+        new_index = 0
+        subdirs = glob(os.path.join(stock_files_directory, "*"))
+        for subdir in subdirs:
+            if os.path.isdir(subdir):
+                new_index += 1
+                add_menu_item(subdir, new_index)
+
+
+    def _do_import_action_on_archive_directories(self, stock_files_directory):
+
+        def add_menu_item(subdir, index_ref):
+            name = os.path.splitext(os.path.basename(subdir))[0]
+            action_name = "stock-dir-%i" % index_ref
+            path = "/MenuBar/radio/stock"
+            action = gtk.Action(action_name,
+                    _("Archive: ") + name,
+                    _("Import stock configuration directory") + (" {name}").format(name=name),
+                    "gtk-directory")
+            action.connect("activate", lambda a, c: self.do_import(start_dir=subdir),"")
+            mid = self.menu_uim.new_merge_id()
+            mid = self.menu_uim.add_ui(mid, path,
+                           action_name, action_name,
+                           gtk.UI_MANAGER_MENUITEM, False)
+            self.menu_ag.add_action(action)
+
+        new_index = 0
+        subdirs = glob(os.path.join(stock_files_directory, "*"))
+        for subdir in subdirs:
+            if os.path.isdir(subdir):
+                new_index += 1
+                add_menu_item(subdir, new_index)
+
+
+    def age_stock_config_file(self, basepath, stock_dir, stock_filename, dir_timestamped):
+
+        READ_ONLY_BINARY='rb'
+
+        #determine if the stock config file already exists
+        # if not, we are done, indicate we handled it.
+        filename1 = os.path.join(basepath, stock_filename)
+        filename2 = os.path.join(stock_dir, stock_filename)
+        if not os.path.exists(filename2):
+            return True
+
+        # calculate and compare checksums
+        checksum1 = ''
+        checksum2 = ''
+
+        with open(filename1,READ_ONLY_BINARY) as f:
+            f_contents=f.read()
+            checksum1 = hashlib.md5(f_contents).hexdigest()
+
+        with open(filename2,READ_ONLY_BINARY) as f:
+            f_contents=f.read()
+            checksum2 = hashlib.md5(f_contents).hexdigest()
+
+        # if the files are the same, we are done
+        # indicate we did not handle it
+        if checksum1 == checksum2:
+            return False
+
+        # now check to see if the ageing directory exists
+        # if not, then create it
+        ageing_directory = os.path.join(stock_dir, dir_timestamped)
+        if not os.path.isdir(ageing_directory):
+            try:
+                os.mkdir(ageing_directory)
+            except Exception, e:
+                return False
+
+        # now try to move the stock config file to ageing
+        try:
+            shutil.move(os.path.join(stock_dir, stock_filename), ageing_directory)
+        except Exception, e:
+            return False
+        return True
+
+
     def _confirm_experimental(self, rclass):
         sql_key = "warn_experimental_%s" % directory.radio_class_id(rclass)
         if CONF.is_defined(sql_key, "state") and \
@@ -834,7 +951,7 @@
 
         return True
 
-    def do_import(self):
+    def do_import(self, start_dir=None):
         types = [(_("All files") + " (*.*)", "*"),
                  (_("CHIRP Files") + " (*.chirp)", "*.chirp"),
                  (_("CHIRP Radio Images") + " (*.img)", "*.img"),
@@ -848,7 +965,10 @@
                  (_("VX5 Commander Files") + " (*.vx5)", "*.vx5"),
                  (_("VX6 Commander Files") + " (*.vx6)", "*.vx6"),
                  (_("VX7 Commander Files") + " (*.vx7)", "*.vx7")]
-        filen = platform.get_platform().gui_open_file(types=types)
+        if not start_dir:
+            filen = platform.get_platform().gui_open_file(types=types)
+        else:
+            filen = platform.get_platform().gui_open_file(start_dir=start_dir, types=types)
         if not filen:
             return
 
@@ -2073,7 +2193,6 @@
                 event.new_window_state == gtk.gdk.WINDOW_STATE_MAXIMIZED,
                 "state")
         self.connect("window-state-event", state_change)
-
         d = CONF.get("last_dir", "state")
         if d and os.path.isdir(d):
             platform.get_platform().set_last_dir(d)


More information about the chirp_devel mailing list