blc2 - Python library and frontend for running theatrical lighting and SFX. Written for the English 2041F fall 2019 theatre production of "The Cenci".

git clone https://benconnors.ca/git-repos/blc2

Log | Files | Refs

pager.py (5068B) - raw


      1 #!/usr/bin/env python3
      2 
      3 import curses 
      4 import threading
      5 
      6 from .globals import CURSES_LOCK
      7 
      8 class Pager:
      9     def set_dim(self, height, width):
     10         if height < 4 or width < 10:
     11             raise ValueError("Size too small")
     12 
     13         with self._lock:
     14             if (height, width) != (self._height, self._width):
     15                 self.win.erase()
     16                 self.win.noutrefresh()
     17 
     18                 self.win.resize(height, width)
     19                 self.win.redrawwin()
     20 
     21             self._height = height
     22             self._width = width
     23             self._regen_actual()
     24             self._redraw()
     25         
     26     def set_pos(self, y, x):
     27         with self._lock:
     28             if (y, x) != (self._y, self._x):
     29                 self.win.mvwin(y, x)
     30                 self._y = y
     31                 self._x = x
     32                 self.win.noutrefresh()
     33 
     34     def _redraw(self):
     35         with self._lock, CURSES_LOCK:
     36             start = max(0, self._bottom_a - self._height + 2)
     37             end = self._bottom_a
     38             todisp = self._actual[start:end]
     39             if len(todisp) < self._height-2 and len(todisp) < len(self._actual):
     40                 end = self._bottom_a+self._height-2-len(todisp)
     41                 todisp += self._actual[self._bottom_a:end]
     42             self.win.erase()
     43             self.win.border()
     44             if start > 0:
     45                 self.win.addch(0, self._width//2, '⯅')
     46             for n, l in enumerate(todisp, 1):
     47                 self.win.addstr(n, 1, l)
     48             if end < len(self._actual):
     49                 self.win.addch(self._height-1, self._width//2, '⯆')
     50             self.win.refresh()
     51 
     52     def _split_line(self, l):
     53         with self._lock:
     54             lines = []
     55             
     56             while len(l) >= self._width-2:
     57                 lines.append(l[:self._width-3] + '…')
     58                 l = "⮡ "+l[self._width-3:]
     59             lines.append(l)
     60 
     61             return lines
     62                 
     63     def _regen_actual(self):
     64         with self._lock:
     65             self._actual = []
     66             for n, l in enumerate(self._lines):
     67                 self._actual += self._split_line(l)
     68                 if n == self._bottom:
     69                     self._bottom_a = len(self._actual)
     70             self._bottom_a = max(self._bottom_a, self._height-2)
     71 
     72     def display_many(self, s, split=False):
     73         with self._lock:
     74             if split and self._lines:
     75                 self._lines.append("")
     76                 self._actual.append("")
     77             for l in s:
     78                 self._lines.append(l)
     79                 self._actual += self._split_line(l)
     80             self._bottom = len(self._lines) - 1
     81             self._bottom_a = len(self._actual)
     82             self._redraw()
     83 
     84     def display(self, s):
     85         self.display_many((s, ))
     86 
     87     def user_page(self, refresh=None):
     88         curses.curs_set(0)
     89         
     90         while True:
     91             l = self.win.getch()
     92 
     93             with self._lock:
     94                 if l == curses.KEY_RESIZE:
     95                     if refresh is not None:
     96                         refresh()
     97                 elif l == ord('q'):
     98                     break
     99                 elif l in (ord('k'), curses.KEY_UP):
    100                     if self._bottom_a > self._height-2:
    101                         self._bottom_a -= 1
    102                         self._redraw()
    103                 elif l in (ord('j'), curses.KEY_DOWN):
    104                     if self._bottom_a < len(self._actual):
    105                         self._bottom_a += 1
    106                         self._redraw()
    107                 elif l == curses.KEY_NPAGE:
    108                     if self._bottom_a < len(self._actual):
    109                         self._bottom_a = min(len(self._actual), self._bottom_a + self._height - 2)
    110                         self._redraw()
    111                 elif l == curses.KEY_PPAGE:
    112                     if self._bottom_a > 0:
    113                         self._bottom_a = max(self._height - 2, self._bottom_a - self._height + 2)
    114                         self._redraw()
    115                 elif l == curses.KEY_HOME:
    116                     if self._bottom_a > self._height - 2:
    117                         self._bottom_a = self._height - 2
    118                         self._redraw()
    119                 elif l == curses.KEY_END:
    120                     if self._bottom_a < len(self._actual):
    121                         self._bottom_a = len(self._actual)
    122                         self._redraw()
    123 
    124         curses.curs_set(1)
    125 
    126     def clear(self):
    127         with self._lock:
    128             self._lines = []
    129             self._actual = []
    130             self._bottom_a = 0
    131             self._bottom = -1
    132             self._redraw()
    133 
    134     def __init__(self, y, x, height, width):
    135         with CURSES_LOCK:
    136             self.win = curses.newwin(height, width, y, x)
    137             self.win.leaveok(True)
    138             self.win.keypad(True)
    139         self._lock = threading.RLock()
    140         self._height = height
    141         self._width = width
    142         self._y = -1
    143         self._x = -1
    144         self._lines = []
    145         self._actual = []
    146         self._bottom = -1
    147         self._bottom_a = 0
    148 
    149         self.set_pos(y, x)
    150         self.set_dim(height, width)