Quellcode durchsuchen

implemented selector.select()

Fabian Peter Hammerle vor 9 Jahren
Ursprung
Commit
509b94a7e3
2 geänderte Dateien mit 175 neuen und 0 gelöschten Zeilen
  1. 8 0
      .gitignore
  2. 167 0
      ioex/selector.py

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+# Compiled python modules.
+*.pyc
+
+# Setuptools distribution folder.
+/dist/
+
+# Python egg metadata, regenerated from source files by setuptools.
+/*.egg-info

+ 167 - 0
ioex/selector.py

@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+
+import os
+import curses
+import curses.textpad
+import curses.wrapper
+import textwrap
+
+class Node(object):
+
+    def __init__(self):
+        self._parent = None
+        self._children = []
+        self._selected = False
+        pass
+
+    def child_count(self):
+        return len(self._children)
+
+    def find_root(self):
+        if self._parent is None:
+            return self
+        else:
+            return self._parent.find_root()
+
+    def find_selected(self):
+        selected_nodes = []
+        if self.selected():
+            selected_nodes.append(self)
+        for child in self.get_children():
+            selected_nodes = selected_nodes + child.find_selected()
+        return selected_nodes
+
+    def get_label(self, active_root):
+        return 'node'
+
+    def get_children(self):
+        return self._children
+
+    def append_child(self, child):
+        child._parent = self
+        self._children.append(child)
+
+    def select(self):
+        self._selected = True
+
+    def selected(self):
+        return self._selected
+
+class StaticNode(Node):
+
+    def __init__(self, label):
+        super(StaticNode, self).__init__()
+        self.label = label
+
+    def get_label(self):
+        return self.label
+
+class SelectionPad(object):
+
+    def __init__(self, parent_window):
+        self._pad = curses.newpad(1, 1)
+        self._pad.keypad(1)
+
+    def refresh(self, pminrow, smaxrow, smaxcol):
+        # window.refresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])
+        #   pminrow and pmincol specify the upper left-hand corner of the rectangle to be displayed in the pad. 
+        #   sminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to be displayed on the screen. 
+        #   The lower right-hand corner of the rectangle to be displayed in the pad is calculated from the screen coordinates, since the rectangles must be the same size.
+        self._pad.refresh(pminrow, 0, 0, 0, smaxrow, smaxcol)
+
+    def addstr(self, line_index, col_index, text, attr = 0):
+        self._pad.addstr(line_index, col_index, text, attr)
+
+    def clear(self):
+        self._pad.clear()
+
+    def get_height(self):   
+        return self._pad.getmaxyx()[0]
+
+    def get_width(self):
+        return self._pad.getmaxyx()[1]
+
+    def getch(self):
+        return self._pad.getch()
+
+    def resize(self, nlines, ncols):
+        self._pad.resize(nlines, ncols)
+
+    def resize_height(self, nlines):
+        self.resize(nlines, self.get_width())
+
+    def resize_width(self, ncols):
+        self.resize(self.get_height(), ncols)
+
+def select(stdscr, active_root):
+
+    curses.curs_set(0)
+    pad = SelectionPad(stdscr)
+
+    active_index = 0
+
+    def get_screen_height():
+        return stdscr.getmaxyx()[0]
+
+    def refresh():
+        pad.clear()
+        pad.resize_width(1)
+        pad.resize_height(len(active_root.get_children()))
+        for child_index in range(len(active_root.get_children())):
+            child = active_root.get_children()[child_index]
+            label = child.get_label()
+            if len(label) > pad.get_width():
+                pad.resize_width(len(label))
+            pad.addstr(
+                child_index, 
+                0,
+                label, 
+                (curses.A_UNDERLINE if child_index == active_index else 0)
+                    | (curses.A_BOLD if child.selected() else 0)
+                )
+        pad.refresh(
+            max(
+                0, 
+                min(
+                    active_root.child_count() - get_screen_height(), 
+                    active_index - int(get_screen_height() / 2)
+                    )
+                ),
+            get_screen_height() - 1, 
+            stdscr.getmaxyx()[1] - 1
+            ) 
+
+    while True:
+        refresh()
+        try:
+            key = pad.getch()
+        except KeyboardInterrupt:
+            return None
+        if key == curses.KEY_RESIZE:
+            refresh()
+        elif key in [curses.KEY_DOWN, ord('j')]:
+            active_index = min(active_root.child_count() - 1, active_index + 1)
+        elif key in [curses.KEY_UP, ord('k')]:
+            active_index = max(0, active_index - 1)
+        elif key == curses.KEY_NPAGE:
+            active_index = min(active_root.child_count() - 1, active_index + int(get_screen_height() / 2))
+        elif key == curses.KEY_PPAGE:
+            active_index = max(0, active_index - get_screen_height())
+        elif key in [ord(' '), ord('\n')]:
+            active_root.get_children()[active_index].select()
+            return active_root.find_root().find_selected()
+        else:
+            raise Exception(key)
+
+def select_lorem():
+    import random
+    import string
+    root = StaticNode('root')
+    for i in range(random.randint(128, 128)):
+        root.append_child(StaticNode(str(i).ljust(5) + ''.join(random.choice(string.lowercase + string.uppercase + ' ') for i in range(random.randint(50, 160)))))
+    selection = curses.wrapper(select, root)
+    if selection is None:
+        print('aborted')
+    else:
+        for node in selection:
+            print(node.label)