[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