[chirp_devel] [PATCH] [RFC] Improve sub-device treatment in the UI
Dan Smith
Sat Mar 30 12:57:21 PDT 2013
# HG changeset patch
# User Dan Smith <dsmith at danplanet.com>
# Date 1364673308 25200
# Node ID 05db705f36a2786c3265c16f16d50a1851cbb782
# Parent f081a4909064529f7bd41af6bd4611fe410c1f41
[RFC] Improve sub-device treatment in the UI
This changes how CHIRP exposes devices with sub-devices to the user, moving
from an strange per-sub-device top-level tab to minor tabs along the left.
This means replicating the memories, bank, and bank names tabs once per
sub-device. In order to prevent two radiothreads from trying to talk to
the same live device at the same time, RadioThread is extended to take a
parent thread, which it shares the same runlock with for exclusion.
This is a big mess of a patch, but I'm not sure how else to break it up,
since there is so much interaction between the affected elements. I have
done minimal testing with a few models, but would really appreciate folks
playing with this a bit (both with non-sub-device models, and especially
live models, which I've not yet tested) in order to verify that this does
not break anything.
Feedback please!
diff -r f081a4909064 -r 05db705f36a2 chirpui/bankedit.py
--- a/chirpui/bankedit.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/bankedit.py Sat Mar 30 12:55:08 2013 -0700
@@ -79,8 +79,7 @@
return True
def __init__(self, rthread):
- common.Editor.__init__(self)
- self.rthread = rthread
+ super(BankNameEditor, self).__init__(rthread)
self._bm = rthread.radio.get_bank_model()
types = [(gobject.TYPE_STRING, "key"),
@@ -249,8 +248,8 @@
self.rthread.submit(job)
def __init__(self, rthread, editorset):
- common.Editor.__init__(self)
- self.rthread = rthread
+ super(BankMembershipEditor, self).__init__(rthread)
+
self.editorset = editorset
self._rf = rthread.radio.get_features()
self._bm = rthread.radio.get_bank_model()
diff -r f081a4909064 -r 05db705f36a2 chirpui/common.py
--- a/chirpui/common.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/common.py Sat Mar 30 12:55:08 2013 -0700
@@ -34,9 +34,11 @@
root = None
- def __init__(self):
+ def __init__(self, rthread):
gobject.GObject.__init__(self)
+ self.read_only = False
self._focused = False
+ self.rthread = rthread
def is_focused(self):
return self._focused
@@ -56,6 +58,15 @@
def hotkey(self, action):
pass
+ def set_read_only(self, read_only):
+ self.read_only = read_only
+
+ def get_read_only(self):
+ return self.read_only
+
+ def prepare_close(self):
+ pass
+
gobject.type_register(Editor)
def DBG(*args):
@@ -128,16 +139,26 @@
(gobject.TYPE_STRING,)),
}
- def __init__(self, radio):
+ def __init__(self, radio, parent=None):
threading.Thread.__init__(self)
gobject.GObject.__init__(self)
self.__queue = {}
+ if parent:
+ self.__runlock = parent._get_run_lock()
+ self.status = lambda msg: parent.status(msg)
+ else:
+ self.__runlock = threading.Lock()
+ self.status = self._status
+
self.__counter = threading.Semaphore(0)
+ self.__lock = threading.Lock()
+
self.__enabled = True
- self.__lock = threading.Lock()
- self.__runlock = threading.Lock()
self.radio = radio
+ def _get_run_lock(self):
+ return self.__runlock
+
def _qlock(self):
self.__lock.acquire()
@@ -196,7 +217,7 @@
self.__counter.release()
self.__enabled = False
- def status(self, msg):
+ def _status(self, msg):
jobs = 0
for i in dict(self.__queue):
jobs += len(self.__queue[i])
@@ -224,14 +245,14 @@
DBG("Running job at priority %i" % i)
break
self._qunlock()
-
+
if job:
self.lock()
self.status(job.desc)
job.execute(self.radio)
last_job_desc = job.desc
self.unlock()
-
+
print "RadioThread exiting"
def log_exception():
diff -r f081a4909064 -r 05db705f36a2 chirpui/dstaredit.py
--- a/chirpui/dstaredit.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/dstaredit.py Sat Mar 30 12:55:08 2013 -0700
@@ -179,8 +179,7 @@
self.rthread.submit(job)
def __init__(self, rthread):
- common.Editor.__init__(self)
- self.rthread = rthread
+ super(DStarEditor, self).__init__(rthread)
self.loaded = False
diff -r f081a4909064 -r 05db705f36a2 chirpui/editorset.py
--- a/chirpui/editorset.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/editorset.py Sat Mar 30 12:55:08 2013 -0700
@@ -35,6 +35,50 @@
(gobject.TYPE_STRING,)),
}
+ def _make_device_editors(self, device, devrthread, index):
+ key = "memedit%i" % index
+ if isinstance(device, chirp_common.IcomDstarSupport):
+ self.editors[key] = memedit.DstarMemoryEditor(devrthread)
+ else:
+ self.editors[key] = memedit.MemoryEditor(devrthread)
+
+ self.editors[key].connect("usermsg",
+ lambda e, m: self.emit("usermsg", m))
+ self.editors[key].connect("changed", self.editor_changed)
+
+ if self.rf.has_sub_devices:
+ label = (_("Memories (%(variant)s)") %
+ dict(variant=device.VARIANT))
+ else:
+ label = _("Memories")
+ lab = gtk.Label(label)
+ memedit_tab = self.tabs.append_page(self.editors[key].root, lab)
+ self.editors[key].root.show()
+
+ if self.rf.has_bank:
+ key = "bank_members%i" % index
+ self.editors[key] = bankedit.BankMembershipEditor(devrthread, self)
+ if self.rf.has_sub_devices:
+ label = _("Banks (%(variant)s)") % dict(variant=device.VARIANT)
+ else:
+ label = _("Banks")
+ lab = gtk.Label(label)
+ self.tabs.append_page(self.editors[key].root, lab)
+ self.editors[key].root.show()
+ self.editors[key].connect("changed", self.banks_changed)
+
+ if self.rf.has_bank_names:
+ self.editors["bank_names"] = bankedit.BankNameEditor(rthread)
+ if self.rf.has_sub_devices:
+ label = (_("Bank Names (%(variant)s)") %
+ dict(variant=device.VARIANT))
+ else:
+ label = _("Bank Names")
+ lab = gtk.Label(label)
+ self.tabs.append_page(self.editors["bank_names"].root, lab)
+ self.editors["bank_names"].root.show()
+ self.editors["bank_names"].connect("changed", self.banks_changed)
+
def __init__(self, source, parent_window=None, filename=None, tempname=None):
gtk.VBox.__init__(self, True, 0)
@@ -49,48 +93,41 @@
else:
raise Exception("Unknown source type")
- self.rthread = common.RadioThread(self.radio)
- self.rthread.setDaemon(True)
- self.rthread.start()
+ rthread = common.RadioThread(self.radio)
+ rthread.setDaemon(True)
+ rthread.start()
- self.rthread.connect("status", lambda e, m: self.emit("status", m))
+ rthread.connect("status", lambda e, m: self.emit("status", m))
self.tabs = gtk.Notebook()
self.tabs.connect("switch-page", self.tab_selected)
self.tabs.set_tab_pos(gtk.POS_LEFT)
self.editors = {
- "memedit" : None,
"dstar" : None,
- "bank_names" : None,
- "bank_members" : None,
"settings" : None,
}
+ self.rf = self.radio.get_features()
+ if self.rf.has_sub_devices:
+ devices = self.radio.get_sub_devices()
+ else:
+ devices = [self.radio]
+
+ index = 0
+ for device in devices:
+ devrthread = common.RadioThread(device, rthread)
+ devrthread.setDaemon(True)
+ devrthread.start()
+ self._make_device_editors(device, devrthread, index)
+ index += 1
+
if isinstance(self.radio, chirp_common.IcomDstarSupport):
- self.editors["memedit"] = memedit.DstarMemoryEditor(self.rthread)
- self.editors["dstar"] = dstaredit.DStarEditor(self.rthread)
- else:
- self.editors["memedit"] = memedit.MemoryEditor(self.rthread)
+ self.editors["dstar"] = dstaredit.DStarEditor(rthread)
- self.editors["memedit"].connect("usermsg",
- lambda e, m: self.emit("usermsg", m))
+ if self.rf.has_settings:
+ self.editors["settings"] = settingsedit.SettingsEditor(rthread)
- rf = self.radio.get_features()
-
- if rf.has_bank:
- self.editors["bank_members"] = \
- bankedit.BankMembershipEditor(self.rthread, self)
-
- if rf.has_bank_names:
- self.editors["bank_names"] = bankedit.BankNameEditor(self.rthread)
-
- if rf.has_settings:
- self.editors["settings"] = settingsedit.SettingsEditor(self.rthread)
-
- lab = gtk.Label(_("Memories"))
- self.tabs.append_page(self.editors["memedit"].root, lab)
- self.editors["memedit"].root.show()
if self.editors["dstar"]:
lab = gtk.Label(_("D-STAR"))
@@ -98,18 +135,6 @@
self.editors["dstar"].root.show()
self.editors["dstar"].connect("changed", self.dstar_changed)
- if self.editors["bank_names"]:
- lab = gtk.Label(_("Bank Names"))
- self.tabs.append_page(self.editors["bank_names"].root, lab)
- self.editors["bank_names"].root.show()
- self.editors["bank_names"].connect("changed", self.banks_changed)
-
- if self.editors["bank_members"]:
- lab = gtk.Label(_("Banks"))
- self.tabs.append_page(self.editors["bank_members"].root, lab)
- self.editors["bank_members"].root.show()
- self.editors["bank_members"].connect("changed", self.banks_changed)
-
if self.editors["settings"]:
lab = gtk.Label(_("Settings"))
self.tabs.append_page(self.editors["settings"].root, lab)
@@ -118,9 +143,6 @@
self.pack_start(self.tabs)
self.tabs.show()
- # pylint: disable-msg=E1101
- self.editors["memedit"].connect("changed", self.editor_changed)
-
self.label = self.text_label = None
self.make_label()
self.modified = (tempname is not None)
@@ -173,19 +195,21 @@
def dstar_changed(self, *args):
print "D-STAR editor changed"
- memedit = self.editors["memedit"]
dstared = self.editors["dstar"]
- memedit.set_urcall_list(dstared.editor_ucall.get_callsigns())
- memedit.set_repeater_list(dstared.editor_rcall.get_callsigns())
- memedit.prefill()
+ for editor in self.editors.values():
+ if isinstance(editor, memedit.MemoryEditor):
+ editor.set_urcall_list(dstared.editor_ucall.get_callsigns())
+ editor.set_repeater_list(dstared.editor_rcall.get_callsigns())
+ editor.prefill()
if not isinstance(self.radio, chirp_common.LiveRadio):
self.modified = True
self.update_tab()
def banks_changed(self, *args):
print "Banks changed"
- if self.editors["bank_members"]:
- self.editors["bank_members"].banks_changed()
+ for editor in self.editors.values():
+ if isinstance(editor, bankedit.BankMembershipEditor):
+ editor.banks_changed()
if not isinstance(self.radio, chirp_common.LiveRadio):
self.modified = True
self.update_tab()
@@ -194,8 +218,9 @@
if not isinstance(self.radio, chirp_common.LiveRadio):
self.modified = True
self.update_tab()
- if self.editors["bank_members"]:
- self.editors["bank_members"].memories_changed()
+ for editor in self.editors.values():
+ if isinstance(editor, bankedit.BankMembershipEditor):
+ editor.memories_changed()
def get_tab_label(self):
return self.label
@@ -226,7 +251,8 @@
if count > 0:
self.editor_changed()
- gobject.idle_add(self.editors["memedit"].prefill)
+ current_editor = self.get_current_editor()
+ gobject.idle_add(current_editor.prefill)
return count
@@ -251,6 +277,13 @@
raise Exception(_("Internal Error"))
def do_import(self, filen):
+ current_editor = self.get_current_editor()
+ if not isinstance(current_editor, memedit.MemoryEditor):
+ # FIXME: We need a nice message to let the user know that they
+ # need to select the appropriate memory editor tab before doing
+ # and import so that we know which thread and editor to import
+ # into and refresh. This will do for the moment.
+ common.show_error("Memory editor must be selected before import")
try:
src_radio = directory.get_radio_by_image(filen)
except Exception, e:
@@ -338,11 +371,13 @@
self)
def prime(self):
+ # NOTE: this is only called to prime new CSV files, so assume
+ # only one memory editor for now
mem = chirp_common.Memory()
mem.freq = 146010000
def cb(*args):
- gobject.idle_add(self.editors["memedit"].prefill)
+ gobject.idle_add(self.editors["memedit0"].prefill)
job = common.RadioJob(cb, "set_memory", mem)
job.set_desc(_("Priming memory"))
@@ -358,16 +393,25 @@
v.unfocus()
def set_read_only(self, read_only=True):
- self.editors["memedit"].set_read_only(read_only)
-
+ for editor in self.editors.values():
+ editor and editor.set_read_only(read_only)
+
def get_read_only(self):
- return self.editors["memedit"].get_read_only()
+ return self.editors["memedit0"].get_read_only()
def prepare_close(self):
- self.editors["memedit"].prepare_close()
+ for editor in self.editors.values():
+ editor and editor.prepare_close()
def get_current_editor(self):
- for e in self.editors.values():
+ for lab, e in self.editors.items():
if e and self.tabs.page_num(e.root) == self.tabs.get_current_page():
return e
raise Exception("No editor selected?")
+
+ @property
+ def rthread(self):
+ """Magic rthread property to return the rthread of the currently-
+ selected editor"""
+ e = self.get_current_editor()
+ return e.rthread
diff -r f081a4909064 -r 05db705f36a2 chirpui/mainapp.py
--- a/chirpui/mainapp.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/mainapp.py Sat Mar 30 12:55:08 2013 -0700
@@ -300,8 +300,6 @@
# We have to actually instantiate the IC9xICFRadio to get its
# sub-devices
radio = ic9x_icf.IC9xICFRadio(fname)
- devices = radio.get_sub_devices()
- del radio
else:
try:
radio = directory.get_radio_by_image(fname)
@@ -315,39 +313,30 @@
common.show_error(os.path.basename(fname) + ": " + str(e))
return
- if radio.get_features().has_sub_devices:
- devices = radio.get_sub_devices()
- else:
- devices = [radio]
+ first_tab = False
+ try:
+ eset = editorset.EditorSet(radio, self,
+ filename=fname,
+ tempname=tempname)
+ except Exception, e:
+ common.log_exception()
+ common.show_error(
+ _("There was an error opening {fname}: {error}").format(
+ fname=fname,
+ error=e))
+ return
- prio = len(devices)
- first_tab = False
- for device in devices:
- try:
- eset = editorset.EditorSet(device, self,
- filename=fname,
- tempname=tempname)
- except Exception, e:
- common.log_exception()
- common.show_error(
- _("There was an error opening {fname}: {error}").format(
- fname=fname,
- error=error))
- return
-
- eset.set_read_only(read_only)
- self._connect_editorset(eset)
- eset.show()
- tab = self.tabs.append_page(eset, eset.get_tab_label())
- if first_tab:
- self.tabs.set_current_page(tab)
- first_tab = False
+ eset.set_read_only(read_only)
+ self._connect_editorset(eset)
+ eset.show()
+ self.tabs.append_page(eset, eset.get_tab_label())
- if hasattr(eset.rthread.radio, "errors") and \
- eset.rthread.radio.errors:
- msg = _("{num} errors during open:").format(num=len(eset.rthread.radio.errors))
- common.show_error_text(msg,
- "\r\n".join(eset.rthread.radio.errors))
+ if hasattr(eset.rthread.radio, "errors") and \
+ eset.rthread.radio.errors:
+ msg = _("{num} errors during open:").format(
+ num=len(eset.rthread.radio.errors))
+ common.show_error_text(msg,
+ "\r\n".join(eset.rthread.radio.errors))
def do_live_warning(self, radio):
d = gtk.MessageDialog(parent=self, buttons=gtk.BUTTONS_OK)
@@ -369,23 +358,12 @@
d.destroy()
def do_open_live(self, radio, tempname=None, read_only=False):
- if radio.get_features().has_sub_devices:
- devices = radio.get_sub_devices()
- else:
- devices = [radio]
-
- first_tab = True
- for device in devices:
- eset = editorset.EditorSet(device, self, tempname=tempname)
- eset.connect("want-close", self.do_close)
- eset.connect("status", self.ev_status)
- eset.set_read_only(read_only)
- eset.show()
-
- tab = self.tabs.append_page(eset, eset.get_tab_label())
- if first_tab:
- self.tabs.set_current_page(tab)
- first_tab = False
+ eset = editorset.EditorSet(radio, self, tempname=tempname)
+ eset.connect("want-close", self.do_close)
+ eset.connect("status", self.ev_status)
+ eset.set_read_only(read_only)
+ eset.show()
+ self.tabs.append_page(eset, eset.get_tab_label())
if isinstance(radio, chirp_common.LiveRadio):
reporting.report_model_usage(radio, "live", True)
diff -r f081a4909064 -r 05db705f36a2 chirpui/memedit.py
--- a/chirpui/memedit.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/memedit.py Sat Mar 30 12:55:08 2013 -0700
@@ -1162,12 +1162,6 @@
self.prefill()
self._config.set_bool("hide_empty", not show)
- def set_read_only(self, read_only):
- self.read_only = read_only
-
- def get_read_only(self):
- return self.read_only
-
def set_hide_unused(self, hide_unused):
self.hide_unused = hide_unused
self.prefill()
@@ -1227,8 +1221,7 @@
return user_visible
def __init__(self, rthread):
- common.Editor.__init__(self)
- self.rthread = rthread
+ super(MemoryEditor, self).__init__(rthread)
self.defaults = dict(self.defaults)
diff -r f081a4909064 -r 05db705f36a2 chirpui/settingsedit.py
--- a/chirpui/settingsedit.py Thu Mar 28 16:34:23 2013 -0700
+++ b/chirpui/settingsedit.py Sat Mar 30 12:55:08 2013 -0700
@@ -26,8 +26,7 @@
class SettingsEditor(common.Editor):
def __init__(self, rthread):
- common.Editor.__init__(self)
- self._rthread = rthread
+ super(SettingsEditor, self).__init__(rthread)
self.root = gtk.HBox(False, 10)
self._store = gtk.TreeStore(gobject.TYPE_STRING,
@@ -57,7 +56,7 @@
job = common.RadioJob(self._build_ui, "get_settings")
job.set_desc("Getting radio settings")
- self._rthread.submit(job)
+ self.rthread.submit(job)
def _save_settings(self):
if self._top_setting_group is None:
@@ -70,7 +69,7 @@
job = common.RadioJob(setting_cb, "set_settings",
self._top_setting_group)
job.set_desc("Setting radio settings")
- self._rthread.submit(job)
+ self.rthread.submit(job)
def _load_setting(self, value, widget):
if isinstance(value, settings.RadioSettingValueInteger):
More information about the chirp_devel
mailing list