commit 4740f05a17f9ef34bec549ae49d72b400c8b587b Author: Damian Johnson atagar@torproject.org Date: Sun May 1 21:06:01 2011 -0700
Reimplementing all panel pausing functionality
Each panel had a custom implementation for their pausing functionality, using an attribute / buffer pattern to juggle their paused vs unpaused states. This was confusing, particularly in the graph panel where we needed whole GraphStats buffer instances.
This replaces those functions with a far saner implementation in their common util parent to make much of this work transparent. --- src/cli/connections/connPanel.py | 32 ++++----- src/cli/controller.py | 11 ++- src/cli/graphing/bandwidthStats.py | 5 ++ src/cli/graphing/connStats.py | 4 + src/cli/graphing/graphPanel.py | 135 +++++++++++++++--------------------- src/cli/graphing/resourceStats.py | 4 + src/cli/headerPanel.py | 28 +++----- src/cli/logPanel.py | 56 +++++---------- src/util/panel.py | 102 +++++++++++++++++++++++++++ src/util/torTools.py | 2 +- 10 files changed, 219 insertions(+), 160 deletions(-)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py index 7e12232..5f4f036 100644 --- a/src/cli/connections/connPanel.py +++ b/src/cli/connections/connPanel.py @@ -55,8 +55,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._lastUpdate = -1 # time the content was last revised self._isTorRunning = True # indicates if tor is currently running or not - self._isPaused = True # prevents updates if true - self._pauseTime = None # time when the panel was paused + self._haltTime = None # time when tor was stopped self._halt = False # terminates thread if true self._cond = threading.Condition() # used for pausing the thread self.valsLock = threading.RLock() @@ -90,27 +89,19 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._isTorRunning = eventType == torTools.State.INIT
- if self._isPaused or not self._isTorRunning: - if not self._pauseTime: self._pauseTime = time.time() - else: self._pauseTime = None + if self._isTorRunning: self._haltTime = None + else: self._haltTime = time.time()
self.redraw(True)
- def setPaused(self, isPause): + def getPauseTime(self): """ - If true, prevents the panel from updating. + Provides the time Tor stopped if it isn't running. Otherwise this is the + time we were last paused. """
- if not self._isPaused == isPause: - self._isPaused = isPause - - if isPause or not self._isTorRunning: - if not self._pauseTime: self._pauseTime = time.time() - else: self._pauseTime = None - - # redraws so the display reflects any changes between the last update - # and being paused - self.redraw(True) + if self._haltTime: return self._haltTime + else: return panel.Panel.getPauseTime(self)
def setSortOrder(self, ordering = None): """ @@ -180,7 +171,7 @@ class ConnectionPanel(panel.Panel, threading.Thread): while not self._halt: currentTime = time.time()
- if self._isPaused or not self._isTorRunning or currentTime - lastDraw < self._config["features.connection.refreshRate"]: + if self.isPaused() or not self._isTorRunning or currentTime - lastDraw < self._config["features.connection.refreshRate"]: self._cond.acquire() if not self._halt: self._cond.wait(0.2) self._cond.release() @@ -224,7 +215,10 @@ class ConnectionPanel(panel.Panel, threading.Thread): scrollOffset = 3 self.addScrollBar(scrollLoc, scrollLoc + height - detailPanelOffset - 1, len(self._entryLines), 1 + detailPanelOffset)
- currentTime = self._pauseTime if self._pauseTime else time.time() + if self.isPaused() or not self._isTorRunning: + currentTime = self.getPauseTime() + else: currentTime = time.time() + for lineNum in range(scrollLoc, len(self._entryLines)): entryLine = self._entryLines[lineNum]
diff --git a/src/cli/controller.py b/src/cli/controller.py index 8543ccb..08d7b17 100644 --- a/src/cli/controller.py +++ b/src/cli/controller.py @@ -45,8 +45,6 @@ PAGES = [ ["config"], ["torrc"]]
-PAUSEABLE = ["header", "graph", "log", "conn"] - CONFIG = {"log.torrc.readFailed": log.WARN, "features.graph.type": 1, "features.config.prepopulateEditValues": True, @@ -142,6 +140,9 @@ class Popup(panel.Panel): def __init__(self, stdscr, height): panel.Panel.__init__(self, stdscr, "popup", 0, height)
+ def setPaused(self, isPause): + panel.Panel.setPaused(self, isPause, True) + # The following methods are to emulate old panel functionality (this was the # only implementations to use these methods and will require a complete # rewrite when refactoring gets here) @@ -217,7 +218,11 @@ def setPauseState(panels, monitorIsPaused, currentPage, overwrite=False): reguardless of the monitor is paused or not. """
- for key in PAUSEABLE: panels[key].setPaused(overwrite or monitorIsPaused or (key not in PAGES[currentPage] and key not in PAGE_S)) + allPanels = list(PAGE_S) + for pagePanels in PAGES: + allPanels += pagePanels + + for key in allPanels: panels[key].setPaused(overwrite or monitorIsPaused or (key not in PAGES[currentPage] and key not in PAGE_S))
def showMenu(stdscr, popup, title, options, initialSelection): """ diff --git a/src/cli/graphing/bandwidthStats.py b/src/cli/graphing/bandwidthStats.py index 2864dd8..9be52e8 100644 --- a/src/cli/graphing/bandwidthStats.py +++ b/src/cli/graphing/bandwidthStats.py @@ -35,6 +35,7 @@ class BandwidthStats(graphPanel.GraphStats): def __init__(self, config=None): graphPanel.GraphStats.__init__(self)
+ self.inputConfig = config self._config = dict(DEFAULT_CONFIG) if config: config.update(self._config, {"features.graph.bw.accounting.rate": 1}) @@ -73,6 +74,10 @@ class BandwidthStats(graphPanel.GraphStats): if writeTotal and writeTotal.isdigit(): self.initialSecondaryTotal = int(writeTotal) / 1024 # Bytes -> KB
+ def clone(self, newCopy=None): + if not newCopy: newCopy = BandwidthStats(self.inputConfig) + return graphPanel.GraphStats.clone(self, newCopy) + def resetListener(self, conn, eventType): # updates title parameters and accounting status if they changed self._titleStats = [] # force reset of title diff --git a/src/cli/graphing/connStats.py b/src/cli/graphing/connStats.py index 51227b7..7f0dc18 100644 --- a/src/cli/graphing/connStats.py +++ b/src/cli/graphing/connStats.py @@ -20,6 +20,10 @@ class ConnStats(graphPanel.GraphStats): self.resetListener(conn, torTools.State.INIT) # initialize port values conn.addStatusListener(self.resetListener)
+ def clone(self, newCopy=None): + if not newCopy: newCopy = ConnStats() + return graphPanel.GraphStats.clone(self, newCopy) + def resetListener(self, conn, eventType): if eventType == torTools.State.INIT: self.orPort = conn.getOption("ORPort", "0") diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py index e4b493d..238e163 100644 --- a/src/cli/graphing/graphPanel.py +++ b/src/cli/graphing/graphPanel.py @@ -60,7 +60,7 @@ class GraphStats(TorCtl.PostEventListener): time and timescale parameters use the labels defined in UPDATE_INTERVALS. """
- def __init__(self, isPauseBuffer=False): + def __init__(self): """ Initializes parameters needed to present a graph. """ @@ -69,11 +69,7 @@ class GraphStats(TorCtl.PostEventListener):
# panel to be redrawn when updated (set when added to GraphPanel) self._graphPanel = None - - # mirror instance used to track updates when paused - self.isPaused, self.isPauseBuffer = False, isPauseBuffer - if isPauseBuffer: self._pauseBuffer = None - else: self._pauseBuffer = GraphStats(True) + self.isSelected = False
# tracked stats self.tick = 0 # number of processed events @@ -95,6 +91,26 @@ class GraphStats(TorCtl.PostEventListener): self.primaryCounts[i] = (self.maxCol + 1) * [0] self.secondaryCounts[i] = (self.maxCol + 1) * [0]
+ def clone(self, newCopy=None): + """ + Provides a deep copy of this instance. + + Arguments: + newCopy - base instance to build copy off of + """ + + if not newCopy: newCopy = GraphStats() + newCopy.tick = self.tick + newCopy.lastPrimary = self.lastPrimary + newCopy.lastSecondary = self.lastSecondary + newCopy.primaryTotal = self.primaryTotal + newCopy.secondaryTotal = self.secondaryTotal + newCopy.maxPrimary = dict(self.maxPrimary) + newCopy.maxSecondary = dict(self.maxSecondary) + newCopy.primaryCounts = copy.deepcopy(self.primaryCounts) + newCopy.secondaryCounts = copy.deepcopy(self.secondaryCounts) + return newCopy + def eventTick(self): """ Called when it's time to process another event. All graphs use tor BW @@ -109,7 +125,7 @@ class GraphStats(TorCtl.PostEventListener): being redrawn. """
- if self._graphPanel and not self.isPauseBuffer and not self.isPaused: + if self._graphPanel and self.isSelected and not self._graphPanel.isPaused(): # use the minimum of the current refresh rate and the panel's updateRate = UPDATE_INTERVALS[self._graphPanel.updateInterval][1] return (self.tick + 1) % min(updateRate, self.getRefreshRate()) == 0 @@ -165,78 +181,40 @@ class GraphStats(TorCtl.PostEventListener):
pass
- def setPaused(self, isPause): - """ - If true, prevents bandwidth updates from being presented. This is a no-op - if a pause buffer. - """ - - if isPause == self.isPaused or self.isPauseBuffer: return - self.isPaused = isPause - - if self.isPaused: active, inactive = self._pauseBuffer, self - else: active, inactive = self, self._pauseBuffer - self._parameterSwap(active, inactive) - def bandwidth_event(self, event): self.eventTick()
- def _parameterSwap(self, active, inactive): - """ - Either overwrites parameters of pauseBuffer or with the current values or - vice versa. This is a helper method for setPaused and should be overwritten - to append with additional parameters that need to be preserved when paused. - """ - - # The pause buffer is constructed as a GraphStats instance which will - # become problematic if this is overridden by any implementations (which - # currently isn't the case). If this happens then the pause buffer will - # need to be of the requester's type (not quite sure how to do this - # gracefully...). - - active.tick = inactive.tick - active.lastPrimary = inactive.lastPrimary - active.lastSecondary = inactive.lastSecondary - active.primaryTotal = inactive.primaryTotal - active.secondaryTotal = inactive.secondaryTotal - active.maxPrimary = dict(inactive.maxPrimary) - active.maxSecondary = dict(inactive.maxSecondary) - active.primaryCounts = copy.deepcopy(inactive.primaryCounts) - active.secondaryCounts = copy.deepcopy(inactive.secondaryCounts) - def _processEvent(self, primary, secondary): """ Includes new stats in graphs and notifies associated GraphPanel of changes. """
- if self.isPaused: self._pauseBuffer._processEvent(primary, secondary) - else: - isRedraw = self.isNextTickRedraw() + isRedraw = self.isNextTickRedraw() + + self.lastPrimary, self.lastSecondary = primary, secondary + self.primaryTotal += primary + self.secondaryTotal += secondary + + # updates for all time intervals + self.tick += 1 + for i in range(len(UPDATE_INTERVALS)): + lable, timescale = UPDATE_INTERVALS[i]
- self.lastPrimary, self.lastSecondary = primary, secondary - self.primaryTotal += primary - self.secondaryTotal += secondary + self.primaryCounts[i][0] += primary + self.secondaryCounts[i][0] += secondary
- # updates for all time intervals - self.tick += 1 - for i in range(len(UPDATE_INTERVALS)): - lable, timescale = UPDATE_INTERVALS[i] + if self.tick % timescale == 0: + self.maxPrimary[i] = max(self.maxPrimary[i], self.primaryCounts[i][0] / timescale) + self.primaryCounts[i][0] /= timescale + self.primaryCounts[i].insert(0, 0) + del self.primaryCounts[i][self.maxCol + 1:]
- self.primaryCounts[i][0] += primary - self.secondaryCounts[i][0] += secondary - - if self.tick % timescale == 0: - self.maxPrimary[i] = max(self.maxPrimary[i], self.primaryCounts[i][0] / timescale) - self.primaryCounts[i][0] /= timescale - self.primaryCounts[i].insert(0, 0) - del self.primaryCounts[i][self.maxCol + 1:] - - self.maxSecondary[i] = max(self.maxSecondary[i], self.secondaryCounts[i][0] / timescale) - self.secondaryCounts[i][0] /= timescale - self.secondaryCounts[i].insert(0, 0) - del self.secondaryCounts[i][self.maxCol + 1:] - - if isRedraw: self._graphPanel.redraw(True) + self.maxSecondary[i] = max(self.maxSecondary[i], self.secondaryCounts[i][0] / timescale) + self.secondaryCounts[i][0] /= timescale + self.secondaryCounts[i].insert(0, 0) + del self.secondaryCounts[i][self.maxCol + 1:] + + if isRedraw: self._graphPanel.redraw(True)
class GraphPanel(panel.Panel): """ @@ -252,7 +230,7 @@ class GraphPanel(panel.Panel): self.currentDisplay = None # label of the stats currently being displayed self.stats = {} # available stats (mappings of label -> instance) self.showLabel = True # shows top label if true, hides otherwise - self.isPaused = False + self.setPauseAttr("stats")
def getHeight(self): """ @@ -279,7 +257,7 @@ class GraphPanel(panel.Panel): """ Redraws graph panel """
if self.currentDisplay: - param = self.stats[self.currentDisplay] + param = self.getAttr("stats")[self.currentDisplay] graphCol = min((width - 10) / 2, param.maxCol)
primaryColor = uiTools.getColor(param.getColor(True)) @@ -387,21 +365,18 @@ class GraphPanel(panel.Panel): """
if label != self.currentDisplay: - if self.currentDisplay: self.stats[self.currentDisplay].setPaused(True) + if self.currentDisplay: self.stats[self.currentDisplay].isSelected = False
if not label: self.currentDisplay = None elif label in self.stats.keys(): self.currentDisplay = label - self.stats[label].setPaused(self.isPaused) + self.stats[self.currentDisplay].isSelected = True else: raise ValueError("Unrecognized stats label: %s" % label)
- def setPaused(self, isPause): - """ - If true, prevents bandwidth updates from being presented. - """ - - if isPause == self.isPaused: return - self.isPaused = isPause - if self.currentDisplay: self.stats[self.currentDisplay].setPaused(self.isPaused) + def copyAttr(self, attr): + if attr == "stats": + # uses custom clone method to copy GraphStats instances + return dict([(key, self.stats[key].clone()) for key in self.stats]) + else: return panel.Panel.copyAttr(self, isPause)
diff --git a/src/cli/graphing/resourceStats.py b/src/cli/graphing/resourceStats.py index a9a8aee..e028874 100644 --- a/src/cli/graphing/resourceStats.py +++ b/src/cli/graphing/resourceStats.py @@ -14,6 +14,10 @@ class ResourceStats(graphPanel.GraphStats): graphPanel.GraphStats.__init__(self) self.queryPid = torTools.getConn().getMyPid()
+ def clone(self, newCopy=None): + if not newCopy: newCopy = ResourceStats() + return graphPanel.GraphStats.clone(self, newCopy) + def getTitle(self, width): return "System Resources:"
diff --git a/src/cli/headerPanel.py b/src/cli/headerPanel.py index f653299..102ef64 100644 --- a/src/cli/headerPanel.py +++ b/src/cli/headerPanel.py @@ -61,7 +61,6 @@ class HeaderPanel(panel.Panel, threading.Thread):
self._isTorConnected = True self._lastUpdate = -1 # time the content was last revised - self._isPaused = False # prevents updates if true self._halt = False # terminates thread if true self._cond = threading.Condition() # used for pausing the thread
@@ -174,9 +173,9 @@ class HeaderPanel(panel.Panel, threading.Thread):
uptimeLabel = "" if self.vals["tor/startTime"]: - if self._haltTime: + if self.isPaused() or not self._isTorConnected: # freeze the uptime when paused or the tor process is stopped - uptimeLabel = uiTools.getShortTimeLabel(self._haltTime - self.vals["tor/startTime"]) + uptimeLabel = uiTools.getShortTimeLabel(self.getPauseTime() - self.vals["tor/startTime"]) else: uptimeLabel = uiTools.getShortTimeLabel(time.time() - self.vals["tor/startTime"])
@@ -263,21 +262,14 @@ class HeaderPanel(panel.Panel, threading.Thread):
self.valsLock.release()
- def setPaused(self, isPause): + def getPauseTime(self): """ - If true, prevents updates from being presented. + Provides the time Tor stopped if it isn't running. Otherwise this is the + time we were last paused. """
- if not self._isPaused == isPause: - self._isPaused = isPause - if self._isTorConnected: - if isPause: self._haltTime = time.time() - else: self._haltTime = None - - # Redraw now so we'll be displaying the state right when paused - # (otherwise the uptime might be off by a second, and change when - # the panel's redrawn for other reasons). - self.redraw(True) + if self._haltTime: return self._haltTime + else: return panel.Panel.getPauseTime(self)
def run(self): """ @@ -288,7 +280,7 @@ class HeaderPanel(panel.Panel, threading.Thread): while not self._halt: currentTime = time.time()
- if self._isPaused or currentTime - lastDraw < 1 or not self._isTorConnected: + if self.isPaused() or currentTime - lastDraw < 1 or not self._isTorConnected: self._cond.acquire() if not self._halt: self._cond.wait(0.2) self._cond.release() @@ -332,9 +324,7 @@ class HeaderPanel(panel.Panel, threading.Thread):
if eventType == torTools.State.INIT: self._isTorConnected = True - if self._isPaused: self._haltTime = time.time() - else: self._haltTime = None - + self._haltTime = None self._update(True) self.redraw(True) elif eventType == torTools.State.CLOSED: diff --git a/src/cli/logPanel.py b/src/cli/logPanel.py index 86e680f..7c1c19a 100644 --- a/src/cli/logPanel.py +++ b/src/cli/logPanel.py @@ -542,14 +542,13 @@ class LogPanel(panel.Panel, threading.Thread): # collapses duplicate log entries if false, showing only the most recent self.showDuplicates = self._config["features.log.showDuplicateEntries"]
+ self.setPauseAttr("msgLog") # tracks the message log when we're paused self.msgLog = [] # log entries, sorted by the timestamp self.loggedEvents = loggedEvents # events we're listening to self.regexFilter = None # filter for presented log events (no filtering if None) self.lastContentHeight = 0 # height of the rendered content when last drawn self.logFile = None # file log messages are saved to (skipped if None) self.scroll = 0 - self._isPaused = False - self._pauseBuffer = [] # location where messages are buffered if paused
self._lastUpdate = -1 # time the content was last revised self._halt = False # terminates thread if true @@ -557,7 +556,7 @@ class LogPanel(panel.Panel, threading.Thread):
# restricts concurrent write access to attributes used to draw the display # and pausing: - # msgLog, loggedEvents, regexFilter, scroll, _pauseBuffer + # msgLog, loggedEvents, regexFilter, scroll self.valsLock = threading.RLock()
# cached parameters (invalidated if arguments for them change) @@ -654,23 +653,17 @@ class LogPanel(panel.Panel, threading.Thread): log.log(self._config["log.logPanel.logFileWriteFailed"], "Unable to write to log file: %s" % sysTools.getFileErrorMsg(exc)) self.logFile = None
- if self._isPaused: - self.valsLock.acquire() - self._pauseBuffer.insert(0, event) - self._trimEvents(self._pauseBuffer) - self.valsLock.release() - else: - self.valsLock.acquire() - self.msgLog.insert(0, event) - self._trimEvents(self.msgLog) - - # notifies the display that it has new content - if not self.regexFilter or self.regexFilter.search(event.getDisplayMessage()): - self._cond.acquire() - self._cond.notifyAll() - self._cond.release() - - self.valsLock.release() + self.valsLock.acquire() + self.msgLog.insert(0, event) + self._trimEvents(self.msgLog) + + # notifies the display that it has new content + if not self.regexFilter or self.regexFilter.search(event.getDisplayMessage()): + self._cond.acquire() + self._cond.notifyAll() + self._cond.release() + + self.valsLock.release()
def _registerArmEvent(self, level, msg, eventTime): eventColor = RUNLEVEL_EVENT_COLOR[level] @@ -763,29 +756,16 @@ class LogPanel(panel.Panel, threading.Thread): self.redraw(True) self.valsLock.release()
- def setPaused(self, isPause): - """ - If true, prevents message log from being updated with new events. - """ - - if isPause == self._isPaused: return - - self._isPaused = isPause - if self._isPaused: self._pauseBuffer = [] - else: - self.valsLock.acquire() - self.msgLog = (self._pauseBuffer + self.msgLog)[:self._config["cache.logPanel.size"]] - self.redraw(True) - self.valsLock.release() - def draw(self, width, height): """ Redraws message log. Entries stretch to use available space and may contain up to two lines. Starts with newest entries. """
+ currentLog = self.getAttr("msgLog") + self.valsLock.acquire() - self._lastLoggedEvents, self._lastUpdate = list(self.msgLog), time.time() + self._lastLoggedEvents, self._lastUpdate = list(currentLog), time.time()
# draws the top label self.addstr(0, 0, self._getTitle(width), curses.A_STANDOUT) @@ -806,7 +786,7 @@ class LogPanel(panel.Panel, threading.Thread): dividerAttr, duplicateAttr = curses.A_BOLD | uiTools.getColor("yellow"), curses.A_BOLD | uiTools.getColor("green")
isDatesShown = self.regexFilter == None and self._config["features.log.showDateDividers"] - eventLog = getDaybreaks(self.msgLog, self._isPaused) if isDatesShown else list(self.msgLog) + eventLog = getDaybreaks(currentLog, self.isPaused()) if isDatesShown else list(currentLog) if not self.showDuplicates: deduplicatedLog = getDuplicates(eventLog)
@@ -950,7 +930,7 @@ class LogPanel(panel.Panel, threading.Thread): maxLogUpdateRate = self._config["features.log.maxRefreshRate"] / 1000.0
sleepTime = 0 - if (self.msgLog == self._lastLoggedEvents and lastDay == currentDay) or self._isPaused: + if (self.msgLog == self._lastLoggedEvents and lastDay == currentDay) or self.isPaused(): sleepTime = 5 elif timeSinceReset < maxLogUpdateRate: sleepTime = max(0.05, maxLogUpdateRate - timeSinceReset) diff --git a/src/util/panel.py b/src/util/panel.py index f286622..4f8763c 100644 --- a/src/util/panel.py +++ b/src/util/panel.py @@ -3,6 +3,8 @@ Wrapper for safely working with curses subwindows. """
import sys +import copy +import time import traceback import curses from threading import RLock @@ -59,6 +61,16 @@ class Panel(): self.panelName = name self.parent = parent self.visible = True + + # Attributes for pausing. The pauseAttr contains variables our getAttr + # method is tracking, and the pause buffer has copies of the values from + # when we were last unpaused (unused unless we're paused). + + self.paused = False + self.pauseAttr = [] + self.pauseBuffer = {} + self.pauseTime = -1 + self.top = top self.height = height self.width = width @@ -117,6 +129,96 @@ class Panel():
self.visible = isVisible
+ def isPaused(self): + """ + Provides if the panel's configured to be paused or not. + """ + + return self.paused + + def setPauseAttr(self, attr): + """ + Configures the panel to track the given attribute so that getAttr provides + the value when it was last unpaused (or its current value if we're + currently unpaused). For instance... + + > self.setPauseAttr("myVar") + > self.myVar = 5 + > self.myVar = 6 # self.getAttr("myVar") -> 6 + > self.setPaused(True) + > self.myVar = 7 # self.getAttr("myVar") -> 6 + > self.setPaused(False) + > self.myVar = 7 # self.getAttr("myVar") -> 7 + + Arguments: + attr - parameter to be tracked for getAttr + """ + + self.pauseAttr.append(attr) + self.pauseBuffer[attr] = self.copyAttr(attr) + + def getAttr(self, attr): + """ + Provides the value of the given attribute when we were last unpaused. If + we're currently unpaused then this is the current value. If untracked this + returns None. + + Arguments: + attr - local variable to be returned + """ + + if not attr in self.pauseAttr: return None + elif self.isPaused(): return self.pauseBuffer[attr] + else: return self.__dict__.get(attr) + + def copyAttr(self, attr): + """ + Provides a duplicate of the given configuration value, suitable for the + pause buffer. + + Arguments: + attr - parameter to be provided back + """ + + currentValue = self.__dict__.get(attr) + return copy.copy(currentValue) + + def setPaused(self, isPause, suppressRedraw = False): + """ + Toggles if the panel is paused or not. This causes the panel to be redrawn + when toggling is pause state unless told to do otherwise. This is + important when pausing since otherwise the panel's display could change + when redrawn for other reasons. + + This returns True if the panel's pause state was changed, False otherwise. + + Arguments: + isPause - freezes the state of the pause attributes if true, makes + them editable otherwise + suppressRedraw - if true then this will never redraw the panel + """ + + if isPause != self.paused: + if isPause: self.pauseTime = time.time() + self.paused = isPause + + if isPause: + # copies tracked attributes so we know what they were before pausing + for attr in self.pauseAttr: + self.pauseBuffer[attr] = self.copyAttr(attr) + + if not suppressRedraw: self.redraw(True) + return True + else: return False + + def getPauseTime(self): + """ + Provides the time that we were last paused, returning -1 if we've never + been paused. + """ + + return self.pauseTime + def getTop(self): """ Provides the position subwindows are placed at within its parent. diff --git a/src/util/torTools.py b/src/util/torTools.py index b527f1e..12af772 100644 --- a/src/util/torTools.py +++ b/src/util/torTools.py @@ -1179,7 +1179,7 @@ class Controller(TorCtl.PostEventListener):
result = None if self.isAlive(): - # determine the nickname if it isn't yet cached + # determine the fingerprint if it isn't yet cached if not relayNickname in self._nicknameToFpLookupCache: # Fingerprints are base64 encoded hex with an extra '='. For instance... # GETINFO ns/name/torexp2 ->