[tor-commits] [nyx/master] Merge LabelPanel into the header

atagar at torproject.org atagar at torproject.org
Tue Mar 22 17:10:28 UTC 2016


commit e79ebcca7f975e4539196a4c4c1150e7179f9335
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Mar 22 10:09:51 2016 -0700

    Merge LabelPanel into the header
    
    The LabelPanel was a one-line panel that usually shows...
    
      page 1 / 4 - m: menu, p: pause, h: page help, q: quit
    
    ... and provides custom messages or prompts for user input. Functionality for
    this was spread across the LabelPanel, Controller, Panel, and popups module.
    Needless to say this can be simplified.
    
    This has always been part of the header so merging a nicer version of it there.
    This is a pretty big overhaul with some unintended consequences - some good,
    some bad...
    
      * Good ones are that our interface now continues to update while awaiting
        keypresses. For example, our graph continues to update when awaiting
        confirmation of something.
    
      * Step back is that we don't quickly redraw overlapped components as we
        previously did, but this is pretty easily fixable.
---
 nyx/controller.py   | 80 +++++++++--------------------------------------------
 nyx/menu/item.py    |  2 +-
 nyx/menu/menu.py    |  8 ++----
 nyx/panel/config.py |  8 +++---
 nyx/panel/graph.py  |  9 ++----
 nyx/panel/header.py | 65 ++++++++++++++++++++++++++++++++++++++-----
 nyx/panel/log.py    | 15 +++++-----
 nyx/popups.py       | 41 ---------------------------
 8 files changed, 90 insertions(+), 138 deletions(-)

diff --git a/nyx/controller.py b/nyx/controller.py
index e469358..5bef1dd 100644
--- a/nyx/controller.py
+++ b/nyx/controller.py
@@ -22,7 +22,7 @@ import stem
 
 from stem.util import conf, log
 
-from nyx.curses import NORMAL, BOLD, HIGHLIGHT
+from nyx.curses import BOLD
 from nyx import tor_controller
 
 
@@ -58,36 +58,17 @@ def get_controller():
   return NYX_CONTROLLER
 
 
-class LabelPanel(nyx.panel.Panel):
-  """
-  Panel that just displays a single line of text.
-  """
+def show_message(message = None, *attr, **kwargs):
+  header_panel = get_controller().get_panel('header')
+  return header_panel.show_message(message, *attr, **kwargs)
 
-  def __init__(self):
-    nyx.panel.Panel.__init__(self, 'msg', height = 1)
-    self.msg_text = ''
-    self.msg_attr = NORMAL
 
-  def set_message(self, msg, attr = None):
-    """
-    Sets the message being displayed by the panel.
+def input_prompt(msg, initial_value = ''):
+  header_panel = get_controller().get_panel('header')
+  return header_panel.input_prompt(msg, initial_value)
 
-    Arguments:
-      msg  - string to be displayed
-      attr - attribute for the label, normal text if undefined
-    """
 
-    if attr is None:
-      attr = NORMAL
-
-    self.msg_text = msg
-    self.msg_attr = attr
-
-  def draw(self, width, height):
-    self.addstr(0, 0, self.msg_text, self.msg_attr)
-
-
-class Controller:
+class Controller(object):
   """
   Tracks the global state of the interface
   """
@@ -98,10 +79,7 @@ class Controller:
     top to bottom on the page.
     """
 
-    self._sticky_panels = [
-      nyx.panel.header.HeaderPanel(),
-      LabelPanel(),
-    ]
+    self._sticky_panels = [nyx.panel.header.HeaderPanel()]
 
     self._page_panels, first_page_panels = [], []
 
@@ -128,7 +106,6 @@ class Controller:
     self._is_paused = False
     self._force_redraw = False
     self._last_drawn = 0
-    self.set_msg()  # initializes our control message
 
   def get_page_count(self):
     """
@@ -159,7 +136,7 @@ class Controller:
     if page_number != self._page:
       self._page = page_number
       self._force_redraw = True
-      self.set_msg()
+      self.get_panel('header').redraw(True)
 
   def next_page(self):
     """
@@ -190,7 +167,7 @@ class Controller:
     if is_pause != self._is_paused:
       self._is_paused = is_pause
       self._force_redraw = True
-      self.set_msg()
+      self.get_panel('header').redraw(True)
 
       for panel_impl in self.get_all_panels():
         panel_impl.set_paused(is_pause)
@@ -303,37 +280,6 @@ class Controller:
     if force:
       self._last_drawn = current_time
 
-  def set_msg(self, msg = None, attr = None, redraw = False):
-    """
-    Sets the message displayed in the interfaces control panel. This uses our
-    default prompt if no arguments are provided.
-
-    Arguments:
-      msg    - string to be displayed
-      attr   - attribute for the label, normal text if undefined
-      redraw - redraws right away if true, otherwise redraws when display
-               content is next normally drawn
-    """
-
-    if msg is None:
-      msg = ''
-
-      if attr is None:
-        if not self._is_paused:
-          msg = 'page %i / %i - m: menu, p: pause, h: page help, q: quit' % (self._page + 1, len(self._page_panels))
-          attr = NORMAL
-        else:
-          msg = 'Paused'
-          attr = HIGHLIGHT
-
-    control_panel = self.get_panel('msg')
-    control_panel.set_message(msg, attr)
-
-    if redraw:
-      control_panel.redraw(True)
-    else:
-      self._force_redraw = True
-
   def quit(self):
     self.quit_signal = True
 
@@ -421,7 +367,7 @@ def start_nyx():
 
       if CONFIG['features.confirmQuit']:
         msg = 'Are you sure (q again to confirm)?'
-        confirmation_key = nyx.popups.show_msg(msg, attr = BOLD)
+        confirmation_key = show_message(msg, BOLD, max_wait = 30)
         quit_confirmed = confirmation_key.match('q')
       else:
         quit_confirmed = True
@@ -432,7 +378,7 @@ def start_nyx():
       # provides prompt to confirm that nyx should issue a sighup
 
       msg = "This will reset Tor's internal state. Are you sure (x again to confirm)?"
-      confirmation_key = nyx.popups.show_msg(msg, attr = BOLD)
+      confirmation_key = show_message(msg, BOLD, max_wait = 30)
 
       if confirmation_key in (ord('x'), ord('X')):
         try:
diff --git a/nyx/menu/item.py b/nyx/menu/item.py
index 8efcaf9..026f14a 100644
--- a/nyx/menu/item.py
+++ b/nyx/menu/item.py
@@ -60,9 +60,9 @@ class MenuItem():
 
     if self._callback:
       control = nyx.controller.get_controller()
-      control.set_msg()
       control.redraw()
       self._callback()
+
     return True
 
   def next(self):
diff --git a/nyx/menu/menu.py b/nyx/menu/menu.py
index da22362..9b0dc20 100644
--- a/nyx/menu/menu.py
+++ b/nyx/menu/menu.py
@@ -77,8 +77,6 @@ class MenuCursor:
 def show_menu():
   with nyx.popups.popup_window(1, below_static = False) as (popup, width, height):
     if popup:
-      control = nyx.controller.get_controller()
-
       # generates the menu and uses the initial selection of the first item in
       # the file menu
 
@@ -94,7 +92,7 @@ def show_menu():
 
         # provide a message saying how to close the menu
 
-        control.set_msg('Press m or esc to close the menu.', BOLD, True)
+        nyx.controller.show_message('Press m or esc to close the menu.', BOLD)
 
         # renders the menu bar, noting where the open submenu is positioned
 
@@ -124,9 +122,9 @@ def show_menu():
         # redraws the rest of the interface if we're rendering on it again
 
         if not cursor.is_done():
-          control.redraw()
+          nyx.controller.get_controller().redraw()
 
-  control.set_msg()
+  nyx.controller.show_message()
 
 
 def _draw_submenu(cursor, level, top, left):
diff --git a/nyx/panel/config.py b/nyx/panel/config.py
index 2ad6f8f..4b2881d 100644
--- a/nyx/panel/config.py
+++ b/nyx/panel/config.py
@@ -223,9 +223,9 @@ class ConfigPanel(nyx.panel.Panel):
           if selection == 0:
             try:
               controller.save_conf()
-              nyx.popups.show_msg('Saved configuration to %s' % controller.get_info('config-file', '<unknown>'), 2)
+              nyx.controller.show_message('Saved configuration to %s' % controller.get_info('config-file', '<unknown>'), HIGHLIGHT, max_wait = 2)
             except IOError as exc:
-              nyx.popups.show_msg('Unable to save configuration (%s)' % exc.strerror, 2)
+              nyx.controller.show_message('Unable to save configuration (%s)' % exc.strerror, HIGHLIGHT, max_wait = 2)
 
           break
         elif key.match('esc'):
@@ -243,7 +243,7 @@ class ConfigPanel(nyx.panel.Panel):
     elif key.is_selection():
       selected = self._scroller.selection(self._get_config_options())
       initial_value = selected.value() if selected.is_set() else ''
-      new_value = nyx.popups.input_prompt('%s Value (esc to cancel): ' % selected.name, initial_value)
+      new_value = nyx.controller.input_prompt('%s Value (esc to cancel): ' % selected.name, initial_value)
 
       if new_value != initial_value:
         try:
@@ -260,7 +260,7 @@ class ConfigPanel(nyx.panel.Panel):
           tor_controller().set_conf(selected.name, new_value)
           self.redraw(True)
         except Exception as exc:
-          nyx.popups.show_msg('%s (press any key)' % exc)
+          nyx.controller.show_message('%s (press any key)' % exc, HIGHLIGHT, max_wait = 30)
     elif key.match('a'):
       self._show_all = not self._show_all
       self.redraw(True)
diff --git a/nyx/panel/graph.py b/nyx/panel/graph.py
index 7315bea..212ee85 100644
--- a/nyx/panel/graph.py
+++ b/nyx/panel/graph.py
@@ -474,13 +474,10 @@ class GraphPanel(nyx.panel.Panel):
       * enter / space - set size
     """
 
-    control = nyx.controller.get_controller()
-
     with nyx.curses.CURSES_LOCK:
       try:
         while True:
-          msg = 'press the down/up to resize the graph, and enter when done'
-          control.set_msg(msg, BOLD, True)
+          nyx.controller.show_message('press the down/up to resize the graph, and enter when done', BOLD)
           key = nyx.curses.key_input()
 
           if key.match('down'):
@@ -497,9 +494,9 @@ class GraphPanel(nyx.panel.Panel):
           elif key.is_selection():
             break
 
-          control.redraw()
+          nyx.controller.get_controller().redraw()
       finally:
-        control.set_msg()
+        nyx.controller.show_message()
 
   def handle_key(self, key):
     if key.match('r'):
diff --git a/nyx/panel/header.py b/nyx/panel/header.py
index 0c7aa1b..00d3ff2 100644
--- a/nyx/panel/header.py
+++ b/nyx/panel/header.py
@@ -20,7 +20,7 @@ from stem.control import Listener, State
 from stem.util import conf, log, proc, str_tools, system
 from nyx import msg, tor_controller
 
-from nyx.curses import RED, GREEN, YELLOW, CYAN, WHITE, BOLD
+from nyx.curses import RED, GREEN, YELLOW, CYAN, WHITE, BOLD, HIGHLIGHT
 
 MIN_DUAL_COL_WIDTH = 141  # minimum width where we'll show two columns
 SHOW_FD_THRESHOLD = 60  # show file descriptor usage if usage is over this percentage
@@ -49,8 +49,51 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
     self._halt = False  # terminates thread if true
     self._reported_inactive = False
 
+    self._message = None
+    self._message_attr = None
+
     tor_controller().add_status_listener(self.reset_listener)
 
+  def show_message(self, message = None, *attr, **kwargs):
+    """
+    Sets the message displayed at the bottom of the header. If not called with
+    anything it clears the override.
+
+    :param str message: message to be displayed
+    :param list attr: text attributes to apply
+    :param int max_wait: seconds to wait for user input, no limit if **None**
+
+    :returns: :class:`~nyx.curses.KeyInput` user pressed if provided a
+      **max_wait**, **None** otherwise or if prompt was canceled
+    """
+
+    self._message = message
+    self._message_attr = attr
+    self.redraw(True)
+
+    if 'max_wait' in kwargs:
+      user_input = nyx.curses.key_input(kwargs['max_wait'])
+      self.show_message()  # clear override
+      return user_input
+
+  def input_prompt(self, message, initial_value = ''):
+    """
+    Prompts the user for input.
+
+    :param str message: prompt for user input
+    :param str initial_value: initial value of the prompt
+
+    :returns: **str** with the user input, this is **None** if the prompt is
+      canceled
+    """
+
+    self.show_message(message)
+    self.redraw(True)
+    user_input = self.getstr(self.get_height() - 1, len(message), initial_value)
+    self.show_message()
+
+    return user_input
+
   def is_wide(self):
     """
     True if we should show two columns of information, False otherwise.
@@ -65,9 +108,9 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
     """
 
     if self._vals.is_relay:
-      return 4 if self.is_wide() else 6
+      return 5 if self.is_wide() else 7
     else:
-      return 3 if self.is_wide() else 4
+      return 4 if self.is_wide() else 5
 
   def send_newnym(self):
     """
@@ -85,7 +128,7 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
     # indication that the signal was sent. Otherwise use a msg.
 
     if not self.is_wide():
-      nyx.popups.show_msg('Requesting a new identity', 1)
+      self.show_message('Requesting a new identity', HIGHLIGHT, max_wait = 1)
 
   def handle_key(self, key):
     if key.match('n'):
@@ -108,15 +151,15 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
         try:
           controller.authenticate()  # TODO: should account for our chroot
         except stem.connection.MissingPassword:
-          password = nyx.popups.input_prompt('Controller Password: ')
+          password = self.input_prompt('Controller Password: ')
 
           if password:
             controller.authenticate(password)
 
         log.notice("Reconnected to Tor's control port")
-        nyx.popups.show_msg('Tor reconnected', 1)
+        self.show_message('Tor reconnected', HIGHLIGHT, max_wait = 1)
       except Exception as exc:
-        nyx.popups.show_msg('Unable to reconnect (%s)' % exc, 3)
+        self.show_message('Unable to reconnect (%s)' % exc, HIGHLIGHT, max_wait = 3)
         controller.close()
     else:
       return False
@@ -156,6 +199,14 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
         self._draw_fingerprint_and_fd_usage(0, 3, left_width, vals)
         self._draw_flags(0, 4, left_width, vals)
 
+    if self._message:
+      self.addstr(height - 1, 0, self._message, *self._message_attr)
+    elif not self.is_paused():
+      controller = nyx.controller.get_controller()
+      self.addstr(height - 1, 0, 'page %i / %i - m: menu, p: pause, h: page help, q: quit' % (controller.get_page() + 1, controller.get_page_count()))
+    else:
+      self.addstr(height - 1, 0, 'Paused', HIGHLIGHT)
+
   def _draw_platform_section(self, x, y, width, vals):
     """
     Section providing the user's hostname, platform, and version information...
diff --git a/nyx/panel/log.py b/nyx/panel/log.py
index 094ad06..c16eaec 100644
--- a/nyx/panel/log.py
+++ b/nyx/panel/log.py
@@ -11,6 +11,7 @@ import threading
 import stem.response.events
 
 import nyx.arguments
+import nyx.controller
 import nyx.curses
 import nyx.panel
 import nyx.popups
@@ -128,7 +129,7 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
     Prompts the user to add a new regex filter.
     """
 
-    regex_input = nyx.popups.input_prompt('Regular expression: ')
+    regex_input = nyx.controller.input_prompt('Regular expression: ')
 
     if regex_input:
       self._filter.select(regex_input)
@@ -153,7 +154,7 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
 
         popup.win.refresh()
 
-        user_input = nyx.popups.input_prompt('Events to log: ')
+        user_input = nyx.controller.input_prompt('Events to log: ')
 
         if user_input:
           try:
@@ -164,21 +165,21 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
               self._event_types = nyx.log.listen_for_events(self._register_tor_event, event_types)
               self.redraw(True)
           except ValueError as exc:
-            nyx.popups.show_msg('Invalid flags: %s' % str(exc), 2)
+            nyx.controller.show_message('Invalid flags: %s' % exc, HIGHLIGHT, max_wait = 2)
 
   def show_snapshot_prompt(self):
     """
     Lets user enter a path to take a snapshot, canceling if left blank.
     """
 
-    path_input = nyx.popups.input_prompt('Path to save log snapshot: ')
+    path_input = nyx.controller.input_prompt('Path to save log snapshot: ')
 
     if path_input:
       try:
         self.save_snapshot(path_input)
-        nyx.popups.show_msg('Saved: %s' % path_input, 2)
+        nyx.controller.show_message('Saved: %s' % path_input, HIGHLIGHT, max_wait = 2)
       except IOError as exc:
-        nyx.popups.show_msg('Unable to save snapshot: %s' % exc, 2)
+        nyx.controller.show_message('Unable to save snapshot: %s' % exc, HIGHLIGHT, max_wait = 2)
 
   def clear(self):
     """
@@ -233,7 +234,7 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
       self.redraw(True)
     elif key.match('c'):
       msg = 'This will clear the log. Are you sure (c again to confirm)?'
-      key_press = nyx.popups.show_msg(msg, attr = BOLD)
+      key_press = nyx.controller.show_message(msg, BOLD, max_wait = 30)
 
       if key_press.match('c'):
         self.clear()
diff --git a/nyx/popups.py b/nyx/popups.py
index bf40e4c..1f19189 100644
--- a/nyx/popups.py
+++ b/nyx/popups.py
@@ -75,47 +75,6 @@ def popup_window(height = -1, width = -1, top = 0, left = 0, below_static = True
   return _Popup()
 
 
-def input_prompt(msg, initial_value = ''):
-  """
-  Prompts the user to enter a string on the control line (which usually
-  displays the page number and basic controls).
-
-  Arguments:
-    msg          - message to prompt the user for input with
-    initial_value - initial value of the field
-  """
-
-  with nyx.curses.CURSES_LOCK:
-    control = nyx.controller.get_controller()
-    msg_panel = control.get_panel('msg')
-    msg_panel.set_message(msg)
-    msg_panel.redraw(True)
-    user_input = msg_panel.getstr(0, len(msg), initial_value)
-    control.set_msg()
-
-    return user_input
-
-
-def show_msg(msg, max_wait = None, attr = HIGHLIGHT):
-  """
-  Displays a single line message on the control line for a set time. Pressing
-  any key will end the message. This returns the key pressed.
-
-  Arguments:
-    msg     - message to be displayed to the user
-    max_wait - time to show the message, indefinite if None
-    attr    - attributes with which to draw the message
-  """
-
-  with nyx.curses.CURSES_LOCK:
-    control = nyx.controller.get_controller()
-    control.set_msg(msg, attr, True)
-
-    key_press = nyx.curses.key_input(max_wait)
-    control.set_msg()
-    return key_press
-
-
 def show_help_popup():
   """
   Presents a popup with instructions for the current page's hotkeys. This



More information about the tor-commits mailing list