commit 62c76582c3816e58f1108ffd095ca8aae92105fb Author: Damian Johnson atagar@torproject.org Date: Sun Jun 12 13:18:36 2011 -0700
Binding handlers for the log submenu --- src/cli/controller.py | 19 ++++- src/cli/graphing/graphPanel.py | 11 +--- src/cli/logPanel.py | 160 +++++++++++++++++++++++++++------------- src/cli/menu/actions.py | 56 +++++++++++---- src/cli/menu/menu.py | 4 +- 5 files changed, 168 insertions(+), 82 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py index cf17da3..6158ade 100644 --- a/src/cli/controller.py +++ b/src/cli/controller.py @@ -288,12 +288,27 @@ class Controller:
return allPanels
- def requestRedraw(self): + def requestRedraw(self, immediate = False): """ Requests that all content is redrawn when the interface is next rendered. + + Arguments: + immediate - redraws now if true, otherwise waits for when next normally + drawn """
- self._forceRedraw = True + if immediate: + displayPanels = self.getDisplayPanels() + + occupiedContent = 0 + for panelImpl in displayPanels: + panelImpl.setTop(occupiedContent) + occupiedContent += panelImpl.getHeight() + + for panelImpl in displayPanels: + panelImpl.redraw(True) + else: + self._forceRedraw = True
def isRedrawRequested(self, clearFlag = False): """ diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py index 69b9121..8c3adf7 100644 --- a/src/cli/graphing/graphPanel.py +++ b/src/cli/graphing/graphPanel.py @@ -307,16 +307,7 @@ class GraphPanel(panel.Panel): panel.CURSES_LOCK.acquire() try: while True: - # redraws the resized panels - displayPanels = control.getDisplayPanels() - - occupiedContent = 0 - for panelImpl in displayPanels: - panelImpl.setTop(occupiedContent) - occupiedContent += panelImpl.getHeight() - - for panelImpl in displayPanels: - panelImpl.redraw(True) + control.requestRedraw(True)
msg = "press the down/up to resize the graph, and enter when done" control.setMsg(msg, curses.A_BOLD, True) diff --git a/src/cli/logPanel.py b/src/cli/logPanel.py index 6d54ab4..ef372ab 100644 --- a/src/cli/logPanel.py +++ b/src/cli/logPanel.py @@ -13,6 +13,7 @@ import threading from TorCtl import TorCtl
import popups +import cli.controller from version import VERSION from util import conf, log, panel, sysTools, torTools, uiTools
@@ -668,6 +669,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
+ def setDuplicateVisability(self, isVisible): + """ + Sets if duplicate log entries are collaped or expanded. + + Arguments: + isVisible - if true all log entries are shown, otherwise they're + deduplicated + """ + + self.showDuplicates = isVisible + def registerEvent(self, event): """ Notes event and redraws log. If paused it's held in a temporary buffer. @@ -728,6 +740,13 @@ class LogPanel(panel.Panel, threading.Thread): self.redraw(True) self.valsLock.release()
+ def getFilter(self): + """ + Provides our currently selected regex filter. + """ + + return self.filterOptions[0] if self.regexFilter else None + def setFilter(self, logFilter): """ Filters log entries according to the given regular expression. @@ -744,6 +763,90 @@ class LogPanel(panel.Panel, threading.Thread): self.redraw(True) self.valsLock.release()
+ def makeFilterSelection(self, selectedOption): + """ + Makes the given filter selection, applying it to the log and reorganizing + our filter selection. + + Arguments: + selectedOption - regex filter we've already added, None if no filter + should be applied + """ + + if selectedOption: + try: + self.setFilter(re.compile(selectedOption)) + + # move selection to top + self.filterOptions.remove(selectedOption) + self.filterOptions.insert(0, selectedOption) + except re.error, exc: + # shouldn't happen since we've already checked validity + msg = "Invalid regular expression ('%s': %s) - removing from listing" % (selectedOption, exc) + log.log(log.WARN, msg) + self.filterOptions.remove(selectedOption) + else: self.setFilter(None) + + def showFilterPrompt(self): + """ + Prompts the user to add a new regex filter. + """ + + cli.controller.getController().requestRedraw(True) + regexInput = popups.inputPrompt("Regular expression: ") + + if regexInput: + try: + self.setFilter(re.compile(regexInput)) + if regexInput in self.filterOptions: self.filterOptions.remove(regexInput) + self.filterOptions.insert(0, regexInput) + except re.error, exc: + popups.showMsg("Unable to compile expression: %s" % exc, 2) + + def showEventSelectionPrompt(self): + """ + Prompts the user to select the events being listened for. + """ + + # allow user to enter new types of events to log - unchanged if left blank + cli.controller.getController().requestRedraw(True) + popup, width, height = popups.init(11, 80) + + if popup: + try: + # displays the available flags + popup.win.box() + popup.addstr(0, 0, "Event Types:", curses.A_STANDOUT) + eventLines = EVENT_LISTING.split("\n") + + for i in range(len(eventLines)): + popup.addstr(i + 1, 1, eventLines[i][6:]) + + popup.win.refresh() + + userInput = popups.inputPrompt("Events to log: ") + if userInput: + userInput = userInput.replace(' ', '') # strips spaces + try: self.setLoggedEvents(expandEvents(userInput)) + except ValueError, exc: + popups.showMsg("Invalid flags: %s" % str(exc), 2) + finally: popups.finalize() + + def showSnapshotPrompt(self): + """ + Lets user enter a path to take a snapshot, canceling if left blank. + """ + + cli.controller.getController().requestRedraw(True) + pathInput = popups.inputPrompt("Path to save log snapshot: ") + + if pathInput: + try: + self.saveSnapshot(pathInput) + popups.showMsg("Saved: %s" % pathInput, 2) + except IOError, exc: + popups.showMsg("Unable to save snapshot: %s" % sysTools.getFileErrorMsg(exc), 2) + def clear(self): """ Clears the contents of the event log. @@ -817,66 +920,17 @@ class LogPanel(panel.Panel, threading.Thread): self.setFilter(None) elif selection == len(options) - 1: # selected 'New...' option - prompt user to input regular expression - regexInput = popups.inputPrompt("Regular expression: ") - - if regexInput: - try: - self.setFilter(re.compile(regexInput)) - if regexInput in self.filterOptions: self.filterOptions.remove(regexInput) - self.filterOptions.insert(0, regexInput) - except re.error, exc: - popups.showMsg("Unable to compile expression: %s" % exc, 2) + self.showFilterPrompt() elif selection != -1: - selectedOption = self.filterOptions[selection - 1] - - try: - self.setFilter(re.compile(selectedOption)) - - # move selection to top - self.filterOptions.remove(selectedOption) - self.filterOptions.insert(0, selectedOption) - except re.error, exc: - # shouldn't happen since we've already checked validity - msg = "Invalid regular expression ('%s': %s) - removing from listing" % (selectedOption, exc) - log.log(log.WARN, msg) - self.filterOptions.remove(selectedOption) + self.makeFilterSelection(self.filterOptions[selection - 1]) finally: panel.CURSES_LOCK.release()
if len(self.filterOptions) > MAX_REGEX_FILTERS: del self.filterOptions[MAX_REGEX_FILTERS:] elif key == ord('e') or key == ord('E'): - # allow user to enter new types of events to log - unchanged if left blank - popup, width, height = popups.init(11, 80) - - if popup: - try: - # displays the available flags - popup.win.box() - popup.addstr(0, 0, "Event Types:", curses.A_STANDOUT) - eventLines = EVENT_LISTING.split("\n") - - for i in range(len(eventLines)): - popup.addstr(i + 1, 1, eventLines[i][6:]) - - popup.win.refresh() - - userInput = popups.inputPrompt("Events to log: ") - if userInput: - userInput = userInput.replace(' ', '') # strips spaces - try: self.setLoggedEvents(expandEvents(userInput)) - except ValueError, exc: - popups.showMsg("Invalid flags: %s" % str(exc), 2) - finally: popups.finalize() + self.showEventSelectionPrompt() elif key == ord('a') or key == ord('A'): - # lets user enter a path to take a snapshot, canceling if left blank - pathInput = popups.inputPrompt("Path to save log snapshot: ") - - if pathInput: - try: - self.saveSnapshot(pathInput) - popups.showMsg("Saved: %s" % pathInput, 2) - except IOError, exc: - popups.showMsg("Unable to save snapshot: %s" % sysTools.getFileErrorMsg(exc), 2) + self.showSnapshotPrompt() else: isKeystrokeConsumed = False
return isKeystrokeConsumed diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py index 9d69cb4..c8b8da3 100644 --- a/src/cli/menu/actions.py +++ b/src/cli/menu/actions.py @@ -24,18 +24,8 @@ def makeMenu(): for pagePanel in control.getDisplayPanels(includeSticky = False): if pagePanel.getName() == "graph": baseMenu.add(makeGraphMenu(pagePanel)) - - logsMenu = cli.menu.item.Submenu("Logs") - logsMenu.add(cli.menu.item.MenuItem("Events", None)) - logsMenu.add(cli.menu.item.MenuItem("Clear", None)) - logsMenu.add(cli.menu.item.MenuItem("Save", None)) - logsMenu.add(cli.menu.item.MenuItem("Filter", None)) - - duplicatesSubmenu = cli.menu.item.Submenu("Duplicates") - duplicatesSubmenu.add(cli.menu.item.MenuItem("Hidden", None)) - duplicatesSubmenu.add(cli.menu.item.MenuItem("Visible", None)) - logsMenu.add(duplicatesSubmenu) - baseMenu.add(logsMenu) + elif pagePanel.getName() == "log": + baseMenu.add(makeLogMenu(pagePanel))
connectionsMenu = cli.menu.item.Submenu("Connections") connectionsMenu.add(cli.menu.item.MenuItem("Identity", None)) @@ -117,7 +107,7 @@ def makeGraphMenu(graphPanel): [X] <Stat 1> [ ] <Stat 2> [ ] <Stat 2> - Resize + Resize... Interval (Submenu) Bounds (Submenu)
@@ -138,7 +128,7 @@ def makeGraphMenu(graphPanel): graphMenu.add(cli.menu.item.SelectionMenuItem(label, statGroup, statKey))
# resizing option - graphMenu.add(cli.menu.item.MenuItem("Resize", graphPanel.resizeGraph)) + graphMenu.add(cli.menu.item.MenuItem("Resize...", graphPanel.resizeGraph))
# interval submenu intervalMenu = cli.menu.item.Submenu("Interval") @@ -162,3 +152,41 @@ def makeGraphMenu(graphPanel):
return graphMenu
+def makeLogMenu(logPanel): + """ + Submenu for the log panel, consisting of... + Events... + Snapshot... + Clear + Show / Hide Duplicates + Filter (Submenu) + + Arguments: + logPanel - instance of the log panel + """ + + logMenu = cli.menu.item.Submenu("Log") + + logMenu.add(cli.menu.item.MenuItem("Events...", logPanel.showEventSelectionPrompt)) + logMenu.add(cli.menu.item.MenuItem("Snapshot...", logPanel.showSnapshotPrompt)) + logMenu.add(cli.menu.item.MenuItem("Clear", logPanel.clear)) + + if logPanel.showDuplicates: label, arg = "Hide", False + else: label, arg = "Show", True + logMenu.add(cli.menu.item.MenuItem("%s Duplicates" % label, functools.partial(logPanel.setDuplicateVisability, arg))) + + # filter submenu + filterMenu = cli.menu.item.Submenu("Filter") + filterGroup = cli.menu.item.SelectionGroup(logPanel.makeFilterSelection, logPanel.getFilter()) + + filterMenu.add(cli.menu.item.SelectionMenuItem("None", filterGroup, None)) + + for option in logPanel.filterOptions: + filterMenu.add(cli.menu.item.SelectionMenuItem(option, filterGroup, option)) + + filterMenu.add(cli.menu.item.MenuItem("New...", logPanel.showFilterPrompt)) + logMenu.add(filterMenu) + + return logMenu + + diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py index e0728e8..b411a9c 100644 --- a/src/cli/menu/menu.py +++ b/src/cli/menu/menu.py @@ -110,9 +110,7 @@ def showMenu(): cursor.handleKey(key)
# redraws the rest of the interface if we're rendering on it again - if not cursor.isDone(): - for panelImpl in control.getDisplayPanels(): - panelImpl.redraw(True) + if not cursor.isDone(): control.requestRedraw(True) finally: control.setMsg() cli.popups.finalize()
tor-commits@lists.torproject.org