[chirp_devel] [PATCH] [RFC] Add bitwise browser when developer options are enabled

Dan Smith
Sun Mar 3 13:48:03 PST 2013


# HG changeset patch
# User Dan Smith <dsmith at danplanet.com>
# Date 1362347281 28800
# Node ID c12728b5626151c5bcd74fb9bcb0aa91e2ba51c2
# Parent  665acce0c527014a33f919d5759220f4c96b59cc
[RFC] Add bitwise browser when developer options are enabled

This version allows editing of most of the arbitrary data. Note that
since the only semantic interpretation of a given value at this layer
is its format and size, much damage can be caused to the underlying
image if care is not exercised :)

Also, changes are not automatically sync'd back to the other tabs
(i.e. memories), so you have to hit "go" again on that tab to re-load
the latest data.

diff -r 665acce0c527 -r c12728b56261 chirp/bitwise.py
--- a/chirp/bitwise.py	Sun Mar 03 11:38:21 2013 -0800
+++ b/chirp/bitwise.py	Sun Mar 03 13:48:01 2013 -0800
@@ -188,7 +188,7 @@
 
     def __str__(self):
         if isinstance(self.__items[0], charDataElement):
-            return "".join([x.get_value() for x in self.__items])
+            return repr("".join([x.get_value() for x in self.__items]))[1:-1]
         else:
             return str(self.__items)
 
@@ -248,6 +248,12 @@
     def __iter__(self):
         return iter(self.__items)
 
+    def items(self):
+        index = 0
+        for item in self.__items:
+            yield (str(index), item)
+            index += 1
+
     def size(self):
         size = 0
         for i in self.__items:
@@ -612,6 +618,13 @@
             raise ValueError("Struct size mismatch during set_raw()")
         self._data[self._offset] = buffer
 
+    def __iter__(self):
+        for item in self._generators.values():
+            yield item
+
+    def items(self):
+        return self._generators.items()
+
 class Processor:
 
     _types = {
diff -r 665acce0c527 -r c12728b56261 chirpui/editorset.py
--- a/chirpui/editorset.py	Sun Mar 03 11:38:21 2013 -0800
+++ b/chirpui/editorset.py	Sun Mar 03 13:48:01 2013 -0800
@@ -19,7 +19,7 @@
 
 from chirp import chirp_common, directory, generic_csv, generic_xml
 from chirpui import memedit, dstaredit, bankedit, common, importdialog
-from chirpui import inputdialog, reporting, settingsedit
+from chirpui import inputdialog, reporting, settingsedit, radiobrowser, config
 
 class EditorSet(gtk.VBox):
     __gsignals__ = {
@@ -65,6 +65,7 @@
             "bank_names"   : None,
             "bank_members" : None,
             "settings"     : None,
+            "browser"      : None,
             }
 
         if isinstance(self.radio, chirp_common.IcomDstarSupport):
@@ -88,6 +89,11 @@
         if rf.has_settings:
             self.editors["settings"] = settingsedit.SettingsEditor(self.rthread)
 
+        conf = config.get()
+        if (hasattr(self.rthread.radio, '_memobj') and
+            conf.get_bool("developer", "state")):
+            self.editors["browser"] = radiobrowser.RadioBrowser(self.rthread)
+
         lab = gtk.Label(_("Memories"))
         self.tabs.append_page(self.editors["memedit"].root, lab)
         self.editors["memedit"].root.show()
@@ -115,6 +121,10 @@
             self.tabs.append_page(self.editors["settings"].root, lab)
             self.editors["settings"].root.show()
 
+        if self.editors["browser"]:
+            lab = gtk.Label(_("Browser"))
+            self.tabs.append_page(self.editors["browser"].root, lab)
+
         self.pack_start(self.tabs)
         self.tabs.show()
 
diff -r 665acce0c527 -r c12728b56261 chirpui/radiobrowser.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirpui/radiobrowser.py	Sun Mar 03 13:48:01 2013 -0800
@@ -0,0 +1,295 @@
+import gtk
+import gobject
+import pango
+import re
+import os
+
+from chirp import bitwise
+from chirpui import common
+
+def do_insert_line_with_tags(b, line):
+    def i(text, *tags):
+        b.insert_with_tags_by_name(b.get_end_iter(), text, *tags)
+
+    def ident(name):
+        if "unknown" in name:
+            i(name, 'grey', 'bold')
+        else:
+            i(name, 'bold')
+
+    def nonzero(value):
+        i(value, 'red', 'bold')
+
+    def foo(value):
+        i(value, 'blue', 'bold')
+
+    m = re.match("^( *)([A-z0-9_]+: )(0x[A-F0-9]+) \((.*)\)$", line)
+    if m:
+        i(m.group(1))
+        ident(m.group(2))
+        if m.group(3) == '0x00':
+            i(m.group(3))
+        else:
+            nonzero(m.group(3))
+        i(' (')
+        for char in m.group(4):
+            if char == '1':
+                nonzero(char)
+            else:
+                i(char)
+        i(')')
+        return
+
+    m = re.match("^( *)([A-z0-9_]+: )(.*)$", line)
+    if m:
+        i(m.group(1))
+        ident(m.group(2))
+        i(m.group(3))
+        return
+
+    m = re.match("^(.*} )([A-z0-9_]+)( \()([0-9]+)( bytes at )(0x[A-F0-9]+)",
+                 line)
+    if m:
+        i(m.group(1))
+        ident(m.group(2))
+        i(m.group(3))
+        foo(m.group(4))
+        i(m.group(5))
+        foo(m.group(6))
+        i(")")
+        return
+
+    i(line)
+
+def do_insert_with_tags(buf, text):
+    buf.set_text('')
+    lines = text.split(os.linesep)
+    for line in lines:
+        do_insert_line_with_tags(buf, line)
+        buf.insert_with_tags_by_name(buf.get_end_iter(), os.linesep)
+
+def classname(obj):
+    return str(obj.__class__).split('.')[-1]
+
+def bitwise_type(classname):
+    return classname.split("DataElement")[0]
+
+class FixedEntry(gtk.Entry):
+    def __init__(self, *args, **kwargs):
+        super(FixedEntry, self).__init__(*args, **kwargs)
+        fontdesc = pango.FontDescription("Courier 10")
+        self.modify_font(fontdesc)
+
+class BitwiseEditor(gtk.HBox):
+    def __init__(self, element):
+        super(BitwiseEditor, self).__init__(False, 3)
+        self._element = element
+        self._build_ui()
+
+class IntegerEditor(BitwiseEditor):
+    def _changed(self, entry, base):
+        if not self._update:
+            return
+        value = entry.get_text()
+        if value.startswith("0x"):
+            value = value[2:]
+        self._element.set_value(int(value, base))
+        self._update_entries(skip=entry)
+
+    def _update_entries(self, skip=None):
+        self._update = False
+        for ent, format_spec in self._entries:
+            if ent != skip:
+                ent.set_text(format_spec.format(int(self._element)))
+        self._update = True
+
+    def _build_ui(self):
+        self._entries = []
+        self._update = True
+
+        hexdigits = ((self._element.size() / 4) + 
+                     (self._element.size() % 4 and 1 or 0))
+        formats = [('Hex', 16, '0x{:0%iX}' % hexdigits),
+                   ('Dec', 10, '{:d}'),
+                   ('Bin', 2, '{:0%ib}' % self._element.size())]
+        for name, base, format_spec in formats:
+            lab = gtk.Label(name)
+            self.pack_start(lab, 0, 0, 0)
+            lab.show()
+            int(self._element)
+            ent = FixedEntry()
+            self._entries.append((ent, format_spec))
+            ent.connect('changed', self._changed, base)
+            self.pack_start(ent, 0, 0, 0)
+            ent.show()
+        self._update_entries()
+
+class BCDArrayEditor(BitwiseEditor):
+    def _changed(self, entry, hexent):
+        self._element.set_value(int(entry.get_text()))
+        self._format_hexent(hexent)
+
+    def _format_hexent(self, hexent):
+        value = ""
+        for i in self._element:
+            a, b = i.get_value()
+            value += "%i%i" % (a, b)
+        hexent.set_text(value)
+
+    def _build_ui(self):
+        lab = gtk.Label("Dec")
+        lab.show()
+        self.pack_start(lab, 0, 0, 0)
+        ent = FixedEntry()
+        ent.set_text(str(int(self._element)))
+        ent.show()
+        self.pack_start(ent, 1, 1, 1)
+
+        lab = gtk.Label("Hex")
+        lab.show()
+        self.pack_start(lab, 0, 0, 0)
+
+        hexent = FixedEntry()
+        hexent.show()
+        self.pack_start(hexent, 1, 1, 1)
+        hexent.set_editable(False)
+
+        ent.connect('changed', self._changed, hexent)
+        self._format_hexent(hexent)
+
+class CharArrayEditor(BitwiseEditor):
+    def _changed(self, entry):
+        self._element.set_value(entry.get_text().ljust(len(self._element)))
+
+    def _build_ui(self):
+        ent = FixedEntry(len(self._element))
+        ent.set_text(str(self._element))
+        ent.connect('changed', self._changed)
+        ent.show()
+        self.pack_start(ent, 1, 1, 1)
+
+class OtherEditor(BitwiseEditor):
+    def _build_ui(self):
+        name = classname(self._element)
+        name = bitwise_type(name)
+        if isinstance(self._element, bitwise.arrayDataElement):
+            name += " %s[%i]" % (
+                bitwise_type(classname(self._element[0])),
+                len(self._element))
+
+        l = gtk.Label(name)
+        l.show()
+        self.pack_start(l, 1, 1, 1)
+
+class RadioBrowser(common.Editor):
+    def _build_ui(self):
+        self._display = gtk.Table(20, 2)
+
+        self._store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+        self._tree = gtk.TreeView(self._store)
+
+        rend = gtk.CellRendererText()
+        tvc = gtk.TreeViewColumn('Element', rend, text=0)
+        self._tree.append_column(tvc)
+        self._tree.connect('button_press_event', self._tree_click)
+        self._tree.set_size_request(200, -1)
+
+        self.root = gtk.HBox(False, 3)
+        sw = gtk.ScrolledWindow()
+        sw.add(self._tree)
+        sw.show()
+        self.root.pack_start(sw, 0, 0, 0)
+        sw = gtk.ScrolledWindow()
+        sw.add_with_viewport(self._display)
+        sw.show()
+        self.root.pack_start(sw, 1, 1, 1)
+        self._tree.show()
+        self._display.show()
+        self.root.show()
+
+    def _fill(self, name, obj, parent=None):
+        iter = self._store.append(parent, (name, obj))
+
+        if isinstance(obj, bitwise.structDataElement):
+            for name, item in obj.items():
+                if isinstance(item, bitwise.structDataElement):
+                    self._fill(name, item, iter)
+                elif isinstance(item, bitwise.arrayDataElement):
+                    self._fill("%s[%i]" % (name, len(item)), item, iter)
+        elif isinstance(obj, bitwise.arrayDataElement):
+            i = 0
+            for item in obj:
+                self._fill("%s[%i]" % (name, i), item, iter)
+                i += 1
+
+    def _tree_click(self, view, event):
+        if event.button != 1:
+            return
+
+        index = [0]
+
+        def pack(widget, pos):
+            self._display.attach(widget, pos, pos + 1, index[0], index[0] + 1,
+                                 xoptions=gtk.FILL, yoptions=0)
+
+        def next_row():
+            index[0] += 1
+
+        def abandon(child):
+            self._display.remove(child)
+
+        pathinfo = view.get_path_at_pos(int(event.x), int(event.y))
+        path = pathinfo[0]
+        iter = self._store.get_iter(path)
+        name, obj = self._store.get(iter, 0, 1)
+
+        self._display.foreach(abandon)
+
+        for name, item in obj.items():
+            if item.size() % 8 == 0:
+                name = '<b>%s</b> <small>(%s %i bytes)</small>' % (
+                    name, bitwise_type(classname(item)), item.size() / 8)
+            else:
+                name = '<b>%s</b> <small>(%s %i bits)</small>' % (
+                    name, bitwise_type(classname(item)), item.size())
+            l = gtk.Label(name + "   ")
+            l.set_use_markup(True)
+            l.show()
+            pack(l, 0)
+
+            if (isinstance(item, bitwise.intDataElement) or
+                isinstance(item, bitwise.bcdDataElement)):
+                e = IntegerEditor(item)
+            elif (isinstance(item, bitwise.arrayDataElement) and
+                  isinstance(item[0], bitwise.bcdDataElement)):
+                e = BCDArrayEditor(item)
+            elif (isinstance(item, bitwise.arrayDataElement) and
+                  isinstance(item[0], bitwise.charDataElement)):
+                e = CharArrayEditor(item)
+            else:
+                e = OtherEditor(item)
+            e.show()
+            pack(e, 1)
+            next_row()
+
+
+    def __init__(self, rthread):
+        super(RadioBrowser, self).__init__()
+        self._radio = rthread.radio
+        self._build_ui()
+        self._fill('root', self._radio._memobj)
+
+if __name__ == "__main__":
+    from chirp import *
+    from chirp import directory
+    import sys
+
+    r = directory.get_radio_by_image(sys.argv[1])
+    class Foo:
+        radio = r
+    w = gtk.Window()
+    b = RadioBrowser(Foo)
+    w.set_default_size(1024, 768)
+    w.add(b.root)
+    w.show()
+    gtk.main()



More information about the chirp_devel mailing list