uhm
This commit is contained in:
parent
51e15fb178
commit
3e3b3543b2
35
RAIILock.py
Normal file
35
RAIILock.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
class RAIILock:
|
||||||
|
|
||||||
|
def __init__(self, lock: threading.RLock, defer=False):
|
||||||
|
|
||||||
|
self.__lock = lock
|
||||||
|
self.__defer = defer
|
||||||
|
|
||||||
|
self.__acquired = False
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
|
||||||
|
if self.__defer is False:
|
||||||
|
self.acquire()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
|
||||||
|
self.release()
|
||||||
|
|
||||||
|
def acquire(self):
|
||||||
|
|
||||||
|
self.__lock.acquire()
|
||||||
|
self.__acquired = True
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
|
||||||
|
if self.__acquired:
|
||||||
|
self.__lock.release()
|
||||||
|
self.__acquired = False
|
202
TextCanvas.py
202
TextCanvas.py
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
|
||||||
|
from .RAIILock import RAIILock
|
||||||
|
|
||||||
|
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class TextCanvas:
|
||||||
|
|
||||||
|
__DEFAULT_TERMINAL_WIDTH = 80
|
||||||
|
__DEFAULT_TERMINAL_HEIGHT = 22
|
||||||
|
__TERMINAL_UPDATE_INTERVAL = 1
|
||||||
|
|
||||||
|
__DRAW_RESOLUTION = 3
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.__terminal_width = self.__DEFAULT_TERMINAL_WIDTH
|
||||||
|
self.__terminal_height = self.__DEFAULT_TERMINAL_HEIGHT
|
||||||
|
self.__last_terminal_dimensions_update = time.time()
|
||||||
|
self._update_terminal_dimensions(force=True)
|
||||||
|
|
||||||
|
self.__grid = []
|
||||||
|
self.__render_string = ""
|
||||||
|
self.__is_dirty = True
|
||||||
|
self.__last_render_time = time.time()
|
||||||
|
|
||||||
|
self.__lock = threading.RLock()
|
||||||
|
|
||||||
|
def _update_render_if_dirty(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
if self.__is_dirty:
|
||||||
|
self._update_render(acquire_locks=False)
|
||||||
|
|
||||||
|
def _update_render(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
self.clear(acquire_locks=acquire_locks)
|
||||||
|
self._update_render_string(acquire_locks=acquire_locks)
|
||||||
|
|
||||||
|
self.__last_render_time = time.time()
|
||||||
|
|
||||||
|
self.__is_dirty = False
|
||||||
|
|
||||||
|
def _update_render_string(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
s = ""
|
||||||
|
for line in self.__grid:
|
||||||
|
for c in line:
|
||||||
|
s += c
|
||||||
|
s += "\n"
|
||||||
|
|
||||||
|
# Don't need that last line feed
|
||||||
|
s = s[:-1]
|
||||||
|
|
||||||
|
self.__grid_string = s
|
||||||
|
|
||||||
|
def get(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
self._update_render_if_dirty(acquire_locks=acquire_locks)
|
||||||
|
|
||||||
|
return self.__grid_string
|
||||||
|
|
||||||
|
def print(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
s = self.get(acquire_locks=acquire_locks)
|
||||||
|
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
def draw_text(self, x, y, text, center=False, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
if not isinstance(text, str):
|
||||||
|
text = str(text)
|
||||||
|
|
||||||
|
if len(text) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if center is True:
|
||||||
|
x -= len(text) / 2
|
||||||
|
x = int(x)
|
||||||
|
|
||||||
|
for c in text:
|
||||||
|
self.write(x=x, y=y, character=c, acquire_locks=acquire_locks)
|
||||||
|
x += 1
|
||||||
|
|
||||||
|
def draw_line(self, start_x, start_y, end_x, end_y, character="*", acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
distance_x = math.fabs(end_x - start_x)
|
||||||
|
distance_y = math.fabs(end_y - start_y)
|
||||||
|
if distance_x > distance_y:
|
||||||
|
distance = distance_x
|
||||||
|
else:
|
||||||
|
distance = distance_y
|
||||||
|
|
||||||
|
direction_x = 1 if end_x > start_x else -1
|
||||||
|
direction_y = 1 if end_y > start_y else -1
|
||||||
|
|
||||||
|
steps = round(distance * self.__DRAW_RESOLUTION)
|
||||||
|
if steps == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
increment_x = distance_x / steps
|
||||||
|
increment_x *= direction_x
|
||||||
|
|
||||||
|
increment_y = distance_y / steps
|
||||||
|
increment_y *= direction_y
|
||||||
|
|
||||||
|
pos_x = start_x
|
||||||
|
pos_y = start_y
|
||||||
|
for i in range(steps):
|
||||||
|
|
||||||
|
pos_x_rounded = round(pos_x)
|
||||||
|
pos_y_rounded = round(pos_y)
|
||||||
|
|
||||||
|
self.write(x=pos_x_rounded, y=pos_y_rounded, character=character, acquire_locks=acquire_locks)
|
||||||
|
|
||||||
|
pos_x += increment_x
|
||||||
|
pos_y += increment_y
|
||||||
|
|
||||||
|
def write(self, x, y, character, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
x = round(x)
|
||||||
|
y = round(y)
|
||||||
|
|
||||||
|
if 0 <= y < len(self.__grid):
|
||||||
|
if 0 <= x < len(self.__grid[y]):
|
||||||
|
|
||||||
|
self.__grid[y][x] = character
|
||||||
|
|
||||||
|
self.__is_dirty = True
|
||||||
|
|
||||||
|
def clear(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
self._update_terminal_dimensions(acquire_locks=acquire_locks)
|
||||||
|
|
||||||
|
self.__grid = []
|
||||||
|
|
||||||
|
for y in range(self.__terminal_height):
|
||||||
|
|
||||||
|
self.__grid.append([])
|
||||||
|
|
||||||
|
for x in range(self.__terminal_width):
|
||||||
|
|
||||||
|
self.__grid[y].append(" ")
|
||||||
|
|
||||||
|
self.__is_dirty = True
|
||||||
|
|
||||||
|
def get_dimensions(self, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
height = len(self.__grid)
|
||||||
|
if height == 0:
|
||||||
|
width = 0
|
||||||
|
else:
|
||||||
|
width = len(self.__grid[0])
|
||||||
|
|
||||||
|
return width, height
|
||||||
|
|
||||||
|
def _update_terminal_dimensions(self, force=False, acquire_locks=True):
|
||||||
|
|
||||||
|
with RAIILock(self.__lock, defer=not acquire_locks):
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
if force is not False and now - self.__last_terminal_dimensions_update < self.__TERMINAL_UPDATE_INTERVAL:
|
||||||
|
return
|
||||||
|
self.__last_terminal_dimensions_update = now
|
||||||
|
|
||||||
|
width, height = self.determine_terminal_dimensions()
|
||||||
|
|
||||||
|
self.__terminal_width = width
|
||||||
|
self.__terminal_height = height
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def determine_terminal_dimensions():
|
||||||
|
|
||||||
|
columns, lines = os.get_terminal_size()
|
||||||
|
|
||||||
|
return columns, lines
|
||||||
|
|
Loading…
Reference in New Issue
Block a user