[tor-commits] [arm/release] Connections panel cleanup.

atagar at torproject.org atagar at torproject.org
Sun Sep 25 21:38:21 UTC 2011


commit 68160bc14ee145c3ad736b2a0c33c6dc40ee1829
Author: Kamran Riaz Khan <krkhan at inspirated.com>
Date:   Thu Jul 21 04:09:16 2011 +0500

    Connections panel cleanup.
    
    The older method relied on changing __class__ attributes of CLI entries
    in order to add get_listing_row() methods to them. Changing __class__
    directly is a bad practice, hence GUI entries now *wrap* CLI entries
    instead of inherting them.
    
    Summary:
    * Each CLI entry instance is mapped via cache key to a GUI entry
      instance.
    * The GUI entry instance is stored inside the treestore rows.
    * After every timeout, the entry lines returned by the CLI conn panel
      are mapped to GUI instances (using cache, if applicable) and the
      resulting GUI entry line is searched in treestore. If one isn't
      found, the whole treestore is cleared and refreshed.
    
    The whole treestore refreshing still doesn't sound very nice. Since
    if a circuit is expanded by clicking the drop-down, a new connection
    taking place would close the expander.
    
    One possible approach of addressing the refresh would be to:
    
    * Add new entries to treestore when they aren't found in the latter
    * Delete old entries from treestore when they aren't found in CLI panel
    
    This should theoretically produce 1-1 mapping between CLI panel's
    entryLines and the treestore.
---
 src/gui/arm.xml                  |   30 +++++++------
 src/gui/connections/circEntry.py |   31 ++++++--------
 src/gui/connections/connEntry.py |   42 +++++++++----------
 src/gui/connections/connPanel.py |   87 +++++++++++++++++++++++++-------------
 4 files changed, 107 insertions(+), 83 deletions(-)

diff --git a/src/gui/arm.xml b/src/gui/arm.xml
index 6c206fd..3b28423 100644
--- a/src/gui/arm.xml
+++ b/src/gui/arm.xml
@@ -12,20 +12,6 @@
       <column type="gchararray"/>
     </columns>
   </object>
-  <object class="GtkTreeStore" id="treestore_conn">
-    <columns>
-      <!-- column-name origin -->
-      <column type="gchararray"/>
-      <!-- column-name dest -->
-      <column type="gchararray"/>
-      <!-- column-name time -->
-      <column type="gchararray"/>
-      <!-- column-name type -->
-      <column type="gchararray"/>
-      <!-- column-name foreground -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
   <object class="GtkListStore" id="liststore_log">
     <columns>
       <!-- column-name timestamp_raw -->
@@ -54,6 +40,22 @@
       <column type="gchararray"/>
     </columns>
   </object>
+  <object class="GtkTreeStore" id="treestore_conn">
+    <columns>
+      <!-- column-name origin -->
+      <column type="gchararray"/>
+      <!-- column-name dest -->
+      <column type="gchararray"/>
+      <!-- column-name time -->
+      <column type="gchararray"/>
+      <!-- column-name type -->
+      <column type="gchararray"/>
+      <!-- column-name foreground -->
+      <column type="gchararray"/>
+      <!-- column-name entry_line -->
+      <column type="GObject"/>
+    </columns>
+  </object>
   <object class="GtkWindow" id="window_main">
     <property name="title" translatable="yes">garm</property>
     <property name="default_width">640</property>
diff --git a/src/gui/connections/circEntry.py b/src/gui/connections/circEntry.py
index abbb631..04d3208 100644
--- a/src/gui/connections/circEntry.py
+++ b/src/gui/connections/circEntry.py
@@ -4,38 +4,33 @@ Connection panel entries for client circuits.
 
 import time
 
-from cli.connections import circEntry, entries
+from cli.connections import entries
 from gui.connections import connEntry
 from util import gtkTools, uiTools
 
-class CircEntry(circEntry.CircEntry):
-  @classmethod
-  def convert_to_gui(self, instance):
-    instance.__class__ = self
-
-class CircHeaderLine(circEntry.CircHeaderLine, connEntry.ConnectionLine):
-  @classmethod
-  def convert_to_gui(self, instance):
-    instance.__class__ = self
+class CircHeaderLine(connEntry.ConnectionLine):
+  def __init__(self, cliLine):
+    connEntry.ConnectionLine.__init__(self, cliLine)
 
   def get_listing_row(self, listingType):
     row = connEntry.ConnectionLine.get_listing_row(self, listingType)
     theme = gtkTools.Theme()
-    return row[:-1] + (theme.colors['active'],)
+    return row[:-2] + (theme.colors['active'], self)
+
+class CircLine(connEntry.ConnectionLine):
+  def __init__(self, cliLine):
+    connEntry.ConnectionLine.__init__(self, cliLine)
 
-class CircLine(circEntry.CircLine, connEntry.ConnectionLine):
-  @classmethod
-  def convert_to_gui(self, instance):
-    instance.__class__ = self
+    self.parentIter = None
 
   def get_listing_row(self, listingType):
     dst, etc = "", ""
 
     if listingType == entries.ListingType.IP_ADDRESS:
-      dst = self.getDestinationLabel(100, includeLocale=True)
-      etc = self.foreign.getNickname()
+      dst = self.cliLine.getDestinationLabel(100, includeLocale=True)
+      etc = self.cliLine.foreign.getNickname()
 
     theme = gtkTools.Theme()
 
-    return (dst, etc, self.placementLabel, self.getType(), theme.colors['insensitive'])
+    return (dst, etc, self.cliLine.placementLabel, self.cliLine.getType(), theme.colors['insensitive'], self)
 
diff --git a/src/gui/connections/connEntry.py b/src/gui/connections/connEntry.py
index f8bcc84..6f4baed 100644
--- a/src/gui/connections/connEntry.py
+++ b/src/gui/connections/connEntry.py
@@ -5,36 +5,34 @@ Connection panel entries related to actual connections to or from the system
 
 import time
 
-from cli.connections import connEntry, entries
+import gobject
+
+from cli.connections import entries
 from cli.connections.connEntry import Category, CONFIG
 from util import gtkTools, torTools, uiTools
 
-class ConnectionEntry(connEntry.ConnectionEntry):
-  @classmethod
-  def convert_to_gui(self, instance):
-    instance.__class__ = self
+class ConnectionLine(gobject.GObject):
+  def __init__(self, cliLine):
+    gobject.GObject.__init__(self)
 
-class ConnectionLine(connEntry.ConnectionLine):
-  @classmethod
-  def convert_to_gui(self, instance):
-    instance.__class__ = self
+    self.cliLine = cliLine
 
   def get_listing_row(self, listingType):
     conn = torTools.getConn()
-    myType = self.getType()
-    dstAddress = self.getDestinationLabel(26, includeLocale = True)
-    localPort = ":%s" % self.local.getPort() if self.includePort else ""
+    myType = self.cliLine.getType()
+    dstAddress = self.cliLine.getDestinationLabel(26, includeLocale = True)
+    localPort = ":%s" % self.cliLine.local.getPort() if self.cliLine.includePort else ""
 
     src, dst, etc = "", "", ""
 
     if listingType == entries.ListingType.IP_ADDRESS:
-      myExternalIpAddr = conn.getInfo("address", self.local.getIpAddr())
-      addrDiffer = myExternalIpAddr != self.local.getIpAddr()
+      myExternalIpAddr = conn.getInfo("address", self.cliLine.local.getIpAddr())
+      addrDiffer = myExternalIpAddr != self.cliLine.local.getIpAddr()
 
       isExpansionType = not myType in (Category.SOCKS, Category.HIDDEN, Category.CONTROL)
 
       if isExpansionType: srcAddress = myExternalIpAddr + localPort
-      else: srcAddress = self.local.getIpAddr() + localPort
+      else: srcAddress = self.cliLine.local.getIpAddr() + localPort
 
       if myType in (Category.SOCKS, Category.CONTROL):
         src = dstAddress
@@ -43,19 +41,19 @@ class ConnectionLine(connEntry.ConnectionLine):
         src = srcAddress
         dst = dstAddress
 
-      if addrDiffer and isExpansionType and self.includeExpandedIpAddr and CONFIG["features.connection.showColumn.expandedIp"]:
-        internalAddress = self.local.getIpAddr() + localPort
+      if addrDiffer and isExpansionType and self.cliLine.includeExpandedIpAddr and CONFIG["features.connection.showColumn.expandedIp"]:
+        internalAddress = self.cliLine.local.getIpAddr() + localPort
 
         if myType == Category.INBOUND: (src, dst) =  (src, internalAddress)
         else: (src, dst) =  (internalAddress, src)
 
-      etc = self.getEtcContent(100, listingType)
+      etc = self.cliLine.getEtcContent(100, listingType)
     else:
-      src = "%s:%s" % (self.local.ipAddr, self.local.port)
-      dst = "%s:%s" % (self.foreign.ipAddr, self.foreign.port)
+      src = "%s:%s" % (self.cliLine.local.ipAddr, self.cliLine.local.port)
+      dst = "%s:%s" % (self.cliLine.foreign.ipAddr, self.cliLine.foreign.port)
 
-    timeLabel = uiTools.getTimeLabel(time.time() - self.startTime)
+    timeLabel = uiTools.getTimeLabel(time.time() - self.cliLine.startTime)
     theme = gtkTools.Theme()
 
-    return (src, dst, timeLabel, self.getType(), theme.colors['insensitive'])
+    return (src, dst, timeLabel, self.cliLine.getType(), theme.colors['insensitive'], self)
 
diff --git a/src/gui/connections/connPanel.py b/src/gui/connections/connPanel.py
index 4ab0ed8..55669f9 100644
--- a/src/gui/connections/connPanel.py
+++ b/src/gui/connections/connPanel.py
@@ -17,37 +17,48 @@ from gui.connections import circEntry, connEntry
 from util import connections, sysTools, uiTools, torTools
 from TorCtl import TorCtl
 
-REFRESH_RATE = 3
-
-def convert_to_gui(instance):
-  cliToGuiMap = [ (cliCircEntry.CircEntry, circEntry.CircEntry),
-                  (cliCircEntry.CircHeaderLine, circEntry.CircHeaderLine),
+class GuiConverter:
+  cliToGuiMap = [ (cliCircEntry.CircHeaderLine, circEntry.CircHeaderLine),
                   (cliCircEntry.CircLine, circEntry.CircLine),
-                  (cliConnEntry.ConnectionEntry, connEntry.ConnectionEntry),
                   (cliConnEntry.ConnectionLine, connEntry.ConnectionLine)]
 
-  for (cliClass, guiClass) in cliToGuiMap:
-    if isinstance(instance, cliClass):
-      guiClass.convert_to_gui(instance)
-      break
+  def __init__(self):
+    self._cache = {}
+
+  def __call__(self, cliInstance):
+    cacheKey = self._calculate_cache_key(cliInstance)
+
+    if self._cache.has_key(cacheKey):
+      return self._cache[cacheKey]
+
+    line = None
+
+    for (cliClass, guiClass) in GuiConverter.cliToGuiMap:
+      if isinstance(cliInstance, cliClass):
+        line = guiClass(cliInstance)
+        self._cache[cacheKey] = line
+        break
+
+    return line
 
-def calculate_cache_key(entryLine):
-  local = (entryLine.local.ipAddr, entryLine.local.port)
-  foreign = (entryLine.foreign.ipAddr, entryLine.foreign.port)
+  def _calculate_cache_key(self, entryLine):
+    local = (entryLine.local.ipAddr, entryLine.local.port)
+    foreign = (entryLine.foreign.ipAddr, entryLine.foreign.port)
 
-  return (entryLine.__class__, local, foreign)
+    return (entryLine.__class__, local, foreign)
+
+convert_to_gui = GuiConverter()
 
 class ConnectionPanel(CliConnectionPanel):
   def __init__(self, builder):
     CliConnectionPanel.__init__(self, None)
 
     self.builder = builder
-    self.cache = {}
 
     conn = torTools.getConn()
     torPid = conn.getMyPid()
-    torCmdName = sysTools.getProcessName(torPid, "tor")
-    connections.getResolver(torCmdName, torPid, "tor")
+    torCmdName = sysTools.getProcessName(torPid, 'tor')
+    connections.getResolver(torCmdName, torPid, 'tor')
 
     gobject.idle_add(self._fill_entries)
     gobject.timeout_add(3000, self._timeout_fill_entries)
@@ -70,14 +81,15 @@ class ConnectionPanel(CliConnectionPanel):
 
     # first pass checks whether we have enough entries cached to not update the treeview
     index = 0
-    for line in self._entryLines:
-      convert_to_gui(line)
-      cacheKey = calculate_cache_key(line)
+    for cliLine in self._entryLines:
+      line = convert_to_gui(cliLine)
+
+      treeIter = self._treestore_get_iter(line)
 
-      if self.cache.has_key(cacheKey):
+      if treeIter:
         if not isinstance(line, circEntry.CircLine):
-          timeLabel = "%d s" % (time.time() - line.startTime)
-          treeStore.set_value(self.cache[cacheKey], 2, timeLabel)
+          timeLabel = "%d s" % (time.time() - line.cliLine.startTime)
+          treeStore.set_value(treeIter, 2, timeLabel)
       else:
         break
 
@@ -91,10 +103,10 @@ class ConnectionPanel(CliConnectionPanel):
     treeStore.clear()
     headerIter = None
 
-    for line in self._entryLines:
-      convert_to_gui(line)
+    for cliLine in self._entryLines:
+      line = convert_to_gui(cliLine)
 
-      if isinstance(line, connEntry.ConnectionLine) and line.isUnresolvedApp():
+      if isinstance(line, connEntry.ConnectionLine) and line.cliLine.isUnresolvedApp():
         self._resolveApps()
 
       row = line.get_listing_row(self._listingType)
@@ -107,8 +119,25 @@ class ConnectionPanel(CliConnectionPanel):
       else:
         currentIter = treeStore.append(None, row)
 
-      cacheKey = calculate_cache_key(line)
-      self.cache[cacheKey] = currentIter
-
     self.valsLock.release()
 
+  def _treestore_get_iter(self, line):
+    def match_func(model, treeIter, data):
+      column, key = data
+      value = model.get_value(treeIter, column)
+      return value == key
+
+    def search(model, treeIter, func, data):
+      while treeIter:
+        if func(model, treeIter, data):
+          return treeIter
+        result = search(model, model.iter_children(treeIter), func, data)
+        if result: return result
+        treeIter = model.iter_next(treeIter)
+      return None
+
+    treeStore = self.builder.get_object('treestore_conn')
+    matchIter = search(treeStore, treeStore.iter_children(None), match_func, (5, line))
+
+    return matchIter
+





More information about the tor-commits mailing list