Update drivers: Rename setcolor to defcolor, add real setcolor which ignores color stack.
Application: Add overridable startup() method.
Fix Widget draw clipping.
# -*- coding: utf-8 -*-
import logging
import time
from tuikit.layout import AnchorLayout
class Timer:
def __init__(self):
self.timeouts = []
self.timelast = None
def add_timeout(self, delay, callback):
"""Register callback to be called after delay seconds.
delay -- in seconds, float
callback -- function to be called with no parameters
"""
if not len(self.timeouts):
self.timelast = time.time()
self.timeouts += [[delay, callback]]
def remove_timeout(self, callback):
"""Unregister callback previously registered with add_timeout."""
for timeout in self.timeouts[:]:
if timeout[1] == callback:
self.timeouts.remove(timeout)
def has_timeout(self):
return len(self.timeouts) > 0
def nearest_timeout(self):
if not self.has_timeout():
return None
return min(self.timeouts)[0]
def process_timeouts(self):
if not self.has_timeout():
return
now = time.time()
lasted = now - self.timelast
self.timelast = now
for timeout in self.timeouts[:]:
adjusted_delay = timeout[0] - lasted
if adjusted_delay <= 0.0:
timeout[1]()
self.timeouts.remove(timeout)
else:
timeout[0] = adjusted_delay
class Application:
'''Application class. Defines main loop.'''
def __init__(self, top_layout=AnchorLayout, driver='curses'):
'''Create application.'''
self._setup_logging()
# Top widget
self._top = None
self._timer = Timer()
self.top = top_layout()
self.quit = False
#: Driver class instance (render + input), e.g. DriverCurses.
self.driver = self.get_driver_instance(driver)
def _setup_logging(self):
self.log = logging.getLogger('tuikit')
self.log.setLevel(logging.DEBUG)
handler = logging.FileHandler('./tuikit.log')
formatter = logging.Formatter('%(asctime)s %(levelname)-5s %(message)s', '%y-%m-%d %H:%M:%S')
handler.setFormatter(formatter)
self.log.addHandler(handler)
@property
def top(self):
return self._top
@top.setter
def top(self, value):
self._top = value
self._top.top = value
self._top.timer = self._timer
self._top.add_handler('draw', self._on_top_draw)
def _on_top_draw(self, ev):
ev.driver.erase()
def start(self):
'''Start application. Runs main loop.'''
self.log.info('=== start ===')
self.driver.start(self.main_loop)
def terminate(self):
'''Terminate application.'''
self.quit = True
def main_loop(self):
'''The main loop.'''
self.startup()
self._top._size = self.driver.size # link top widget size to screen size
self._top.emit('resize')
timer = self._timer
while True:
self._top.draw(self.driver, 0, 0)
self.driver.commit()
timeout = timer.nearest_timeout()
events = self.driver.getevents(timeout)
timer.process_timeouts()
for event in events:
if event[0] == 'quit':
self.quit = True
else:
self._top.emit(event[0], *event[1:])
if self.quit:
break
self.log.info('=== quit ===')
def startup(self):
"""This is called after startup, before entering event loop."""
self.apply_theme()
def apply_theme(self):
#TODO: allow custom colors:
# e.g. "blue (#2020FF) on black (#101010), underline"
# color in brackets is used when driver supports custom colors
drv = self.driver
drv.defcolor('normal', 'lightgray on black')
drv.defcolor('strong', 'white on black, bold')
drv.defcolor('active', 'black on cyan')
drv.defcolor('window:normal', 'lightgray on blue')
drv.defcolor('window:controls', 'white on blue, bold')
drv.defcolor('window:controls-active', 'cyan on blue, bold')
drv.defcolor('button', 'black on white')
drv.defcolor('button-active', 'black on cyan')
drv.defcolor('menu', 'black on cyan')
drv.defcolor('menu-active', 'white on cyan, bold')
drv.defcolor('combo:normal', 'black on green')
def get_driver_instance(self, name):
module = __import__('tuikit.driver_' + name, fromlist=['driverclass'])
return module.driverclass()