commit 35206a3a09979f6321aa8e9ce97728b4b1ccf92c Author: Damian Johnson atagar@torproject.org Date: Thu Jul 2 09:57:13 2015 -0700
Rewrite port count dialog
Revising the dialog that shows aggregated counts for entry or exit connections (locales if a guard, or port usage if an exit). This is a pretty small popup and code got even smaller with the cleanup, so moving it into popups.py. --- nyx/connections/__init__.py | 1 - nyx/connections/conn_panel.py | 19 +++++-- nyx/connections/count_popup.py | 107 ---------------------------------------- nyx/popups.py | 51 +++++++++++++++++++ 4 files changed, 67 insertions(+), 111 deletions(-)
diff --git a/nyx/connections/__init__.py b/nyx/connections/__init__.py index 447adf6..c7e90a9 100644 --- a/nyx/connections/__init__.py +++ b/nyx/connections/__init__.py @@ -6,7 +6,6 @@ __all__ = [ 'circ_entry', 'conn_entry', 'conn_panel', - 'count_popup', 'descriptor_popup', 'entries', ] diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py index c555a38..0a27721 100644 --- a/nyx/connections/conn_panel.py +++ b/nyx/connections/conn_panel.py @@ -10,7 +10,7 @@ import threading import nyx.popups import nyx.util.tracker
-from nyx.connections import count_popup, descriptor_popup, entries, conn_entry, circ_entry +from nyx.connections import descriptor_popup, entries, conn_entry, circ_entry from nyx.util import panel, tor_controller, tracker, ui_tools
from stem.control import State @@ -24,6 +24,8 @@ DETAILS_HEIGHT = 7
Listing = enum.Enum(('IP_ADDRESS', 'IP Address'), 'HOSTNAME', 'FINGERPRINT', 'NICKNAME')
+EXIT_USAGE_WIDTH = 15 +
def conf_handler(key, value): if key == 'features.connection.listing_type': @@ -335,9 +337,20 @@ class ConnectionPanel(panel.Panel, threading.Thread): self.set_title_visible(True) self.redraw(True) elif key.match('c') and self.is_clients_allowed(): - count_popup.showCountDialog(count_popup.CountType.CLIENT_LOCALE, self._client_locale_usage) + nyx.popups.show_count_dialog('Client Locales', self._client_locale_usage) elif key.match('e') and self.is_exits_allowed(): - count_popup.showCountDialog(count_popup.CountType.EXIT_PORT, self._exit_port_usage) + counts = {} + key_width = max(map(len, self._exit_port_usage.keys())) + + for k, v in self._exit_port_usage.items(): + usage = connection.port_usage(k) + + if usage: + k = k.ljust(key_width + 3) + usage.ljust(EXIT_USAGE_WIDTH) + + counts[k] = v + + nyx.popups.show_count_dialog('Exiting Port Usage', counts) else: return False
diff --git a/nyx/connections/count_popup.py b/nyx/connections/count_popup.py deleted file mode 100644 index e61dc26..0000000 --- a/nyx/connections/count_popup.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Provides a dialog with client locale or exiting port counts. -""" - -import curses -import operator - -import nyx.controller -import nyx.popups - -from stem.util import connection, enum, log - -CountType = enum.Enum('CLIENT_LOCALE', 'EXIT_PORT') -EXIT_USAGE_WIDTH = 15 - - -def showCountDialog(count_type, counts): - """ - Provides a dialog with bar graphs and percentages for the given set of - counts. Pressing any key closes the dialog. - - Arguments: - count_type - type of counts being presented - counts - mapping of labels to counts - """ - - is_no_stats = not counts - no_stats_msg = "Usage stats aren't available yet, press any key..." - - if is_no_stats: - height, width = 3, len(no_stats_msg) + 4 - else: - height, width = 4 + max(1, len(counts)), 80 - - with nyx.popups.popup_window(height, width) as (popup, height, width): - if popup: - control = nyx.controller.get_controller() - - popup.win.box() - - # dialog title - - if count_type == CountType.CLIENT_LOCALE: - title = 'Client Locales' - elif count_type == CountType.EXIT_PORT: - title = 'Exiting Port Usage' - else: - title = '' - log.warn('Unrecognized count type: %s' % count_type) - - popup.addstr(0, 0, title, curses.A_STANDOUT) - - if is_no_stats: - popup.addstr(1, 2, no_stats_msg, curses.A_BOLD, 'cyan') - else: - sorted_counts = sorted(counts.iteritems(), key=operator.itemgetter(1)) - sorted_counts.reverse() - - # constructs string formatting for the max key and value display width - - key_width, val_width, value_total = 3, 1, 0 - - for k, v in sorted_counts: - key_width = max(key_width, len(k)) - val_width = max(val_width, len(str(v))) - value_total += v - - # extra space since we're adding usage informaion - - if count_type == CountType.EXIT_PORT: - key_width += EXIT_USAGE_WIDTH - - label_format = '%%-%is %%%ii (%%%%%%-2i)' % (key_width, val_width) - - for i in range(height - 4): - k, v = sorted_counts[i] - - # includes a port usage column - - if count_type == CountType.EXIT_PORT: - usage = connection.port_usage(k) - - if usage: - key_format = '%%-%is %%s' % (key_width - EXIT_USAGE_WIDTH) - k = key_format % (k, usage[:EXIT_USAGE_WIDTH - 3]) - - label = label_format % (k, v, v * 100 / value_total) - popup.addstr(i + 1, 2, label, curses.A_BOLD, 'green') - - # All labels have the same size since they're based on the max widths. - # If this changes then this'll need to be the max label width. - - label_width = len(label) - - # draws simple bar graph for percentages - - fill_width = v * (width - 4 - label_width) / value_total - - for j in range(fill_width): - popup.addstr(i + 1, 3 + label_width + j, ' ', curses.A_STANDOUT, 'red') - - popup.addstr(height - 2, 2, 'Press any key...') - - popup.win.refresh() - - curses.cbreak() - control.key_input() diff --git a/nyx/popups.py b/nyx/popups.py index 38eade0..351467f 100644 --- a/nyx/popups.py +++ b/nyx/popups.py @@ -3,12 +3,15 @@ Functions for displaying popups in the interface. """
import curses +import operator
import nyx.controller
from nyx import __version__, __release_date__ from nyx.util import panel, ui_tools
+NO_STATS_MSG = "Usage stats aren't available yet, press any key..." +
def popup_window(height = -1, width = -1, top = 0, left = 0, below_static = True): """ @@ -199,6 +202,54 @@ def show_about_popup(): control.key_input()
+def show_count_dialog(title, counts): + """ + Provides a dialog with bar graphs and percentages for the given set of + counts. Pressing any key closes the dialog. + + :param str title: dialog title + :param dict counts: mapping of labels to their value + """ + + if not counts: + height, width = 3, len(NO_STATS_MSG) + 4 + else: + height, width = 4 + max(1, len(counts)), 80 + + with nyx.popups.popup_window(height, width) as (popup, width, height): + if not popup: + return + + if not counts: + popup.addstr(1, 2, NO_STATS_MSG, curses.A_BOLD, 'cyan') + else: + key_width, val_width, value_total = 3, 1, 0 + + for k, v in counts.items(): + key_width = max(key_width, len(k)) + val_width = max(val_width, len(str(v))) + value_total += v + + sorted_counts = sorted(counts.iteritems(), key = operator.itemgetter(1), reverse = True) + graph_width = width - key_width - val_width - 11 # border, extra spaces, and percentage column + + for y, (k, v) in enumerate(sorted_counts): + label = '%s %s (%-2i%%)' % (k.ljust(key_width), str(v).rjust(val_width), v * 100 / value_total) + x = popup.addstr(y + 1, 2, label, curses.A_BOLD, 'green') + + for j in range(graph_width * v / value_total): + popup.addstr(y + 1, x + j + 1, ' ', curses.A_STANDOUT, 'red') + + popup.addstr(height - 2, 2, 'Press any key...') + + popup.win.box() + popup.addstr(0, 0, title, curses.A_STANDOUT) + popup.win.refresh() + + curses.cbreak() + nyx.controller.get_controller().key_input() + + def show_sort_dialog(title, options, old_selection, option_colors): """ Displays a sorting dialog of the form: