| 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 |
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)