Add timer, adjust inheritance of Widget, Container, Window.
authorRadek Brich <radek.brich@devl.cz>
Wed, 19 Mar 2014 20:42:52 +0100
changeset 90 781774a8d568
parent 89 94f5baef19ac
child 91 de80e140b0ec
Add timer, adjust inheritance of Widget, Container, Window.
tuikit/core/application.py
tuikit/core/buffer.py
tuikit/core/container.py
tuikit/core/timer.py
tuikit/core/widget.py
tuikit/core/window.py
--- a/tuikit/core/application.py	Wed Mar 19 00:32:38 2014 +0100
+++ b/tuikit/core/application.py	Wed Mar 19 20:42:52 2014 +0100
@@ -1,5 +1,6 @@
 from tuikit.core.window import WindowManager, Window
 from tuikit.core.theme import default_theme
+from tuikit.core.timer import Timer
 
 import logging
 
@@ -14,12 +15,10 @@
     """
 
     def __init__(self, driver='curses'):
-        # logger
         self.log = logging.getLogger('tuikit')
-        # Driver
         self.driver = None
-        # Window Manager and root Window
-        self.window_manager = WindowManager()
+        self.timer = Timer()
+        self.window_manager = WindowManager(timer=self.timer)
         self.root_window = Window()
         # flags
         self._started = False
@@ -57,18 +56,18 @@
         """The main loop."""
         self._started = True
         self.window_manager.handle_event('resize', *self.driver.size)
-#        timer = self._timer
 
         while not self._quit:
             self.window_manager.draw(self.driver)
             self.driver.flush()
 
-            #timeout = timer.nearest_timeout()
-            events = self.driver.getevents()#timeout)
-            #timer.process_timeouts()
+            timeout = self.timer.nearest_timeout()
+            events = self.driver.getevents(timeout)
 
+            self.timer.process_timeouts()
             for event in events:
                 self.window_manager.handle_event(event[0], *event[1:])
+
         self._started = False
         self.log.info('=== quit ===')
 
--- a/tuikit/core/buffer.py	Wed Mar 19 00:32:38 2014 +0100
+++ b/tuikit/core/buffer.py	Wed Mar 19 20:42:52 2014 +0100
@@ -107,7 +107,7 @@
 
     @property
     def size(self):
-        return self._size
+        return self._size.readonly()
 
     def resize(self, w, h):
         """Resize buffer."""
--- a/tuikit/core/container.py	Wed Mar 19 00:32:38 2014 +0100
+++ b/tuikit/core/container.py	Wed Mar 19 20:42:52 2014 +0100
@@ -1,30 +1,34 @@
-from tuikit.core.theme import default_theme
+from tuikit.core.widget import Widget
 
 
-class Container:
+class Container(Widget):
+
+    """Container widget.
 
-    """Container for widgets."""
+    Can contain other widgets to create hierarchical structure.
+
+    """
 
     def __init__(self):
+        Widget.__init__(self)
         #: List of child widgets.
         self.children = []
-        self.theme = default_theme
 
-    def add(self, widget, **kwargs):
+    def add(self, widget):
         """Add widget into container."""
         self.children.append(widget)
         widget.parent = self
-        widget.window = self.window if hasattr(self, 'window') else self
+        widget.window = self.window
         widget.set_theme(self.theme)
 
-    def set_theme(self, theme):
-        self.theme = theme
-        for child in self.children:
-            child.set_theme(theme)
-
     def draw(self, buffer, x=0, y=0):
         """Draw child widgets."""
         for child in self.children:
             child.draw(buffer,
                        x + child.x,
                        y + child.y)
+
+    def set_theme(self, theme):
+        Widget.set_theme(self, theme)
+        for child in self.children:
+            child.set_theme(theme)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/core/timer.py	Wed Mar 19 20:42:52 2014 +0100
@@ -0,0 +1,81 @@
+import time
+
+
+class TimerEvent:
+
+    __slots__ = ('time', 'callback', 'args')
+
+    def __init__(self, delay, callback, args):
+        self.time = time.time() + delay
+        self.callback = callback
+        self.args = args
+
+    def execute(self):
+        self.callback(*self.args)
+
+    def __lt__(self, other):
+        return self.time < other.time
+
+    def __str__(self):
+        return '{:.2f}s {} {}'.format(
+            self.time - time.time(), self.callback, self.args)
+
+
+class Timer:
+
+    def __init__(self):
+        #: Each timeout
+        self.events = []
+
+    def add_timeout(self, delay, callback, *args):
+        """Register `callback` to be called after `delay` seconds."""
+        self.events.append(TimerEvent(delay, callback, args))
+        self.events.sort(reverse=True)
+
+    def remove_timeout(self, callback, *args):
+        """Unregister callback previously registered with add_timeout.
+
+        Removes all timeouts with the `callback` and `args`.
+
+        """
+        for event in self.events[:]:
+            if event.callback == callback and event.args == args:
+                self.events.remove(event)
+
+    def nearest_timeout(self):
+        """Get interval in seconds when next timeout expires.
+
+        Returns None when no timeout is registered.
+
+        """
+        if not len(self.events):
+            return None
+        timeout = self.events[-1].time - time.time()
+        return max(0, timeout)
+
+    def process_timeouts(self):
+        """Execute all expired timeouts."""
+        now = time.time()
+        while len(self.events) and self.events[-1].time <= now:
+            self.events[-1].execute()
+            self.events.pop()
+
+    def __str__(self):
+        return '\n'.join(str(event) for event in self.events)
+
+
+if __name__ == '__main__':
+    # Self Test
+    t = Timer()
+    t.process_timeouts()
+    t.add_timeout(1, print, 'timeout 1')
+    t.add_timeout(3, print, 'timeout 3')
+    t.add_timeout(2, print, 'timeout 2')
+    print(t)
+    print('Nearest: %.2f' % t.nearest_timeout())
+    t.process_timeouts()
+    time.sleep(1)
+    t.process_timeouts()
+    print(t)
+    time.sleep(2)
+    t.process_timeouts()
--- a/tuikit/core/widget.py	Wed Mar 19 00:32:38 2014 +0100
+++ b/tuikit/core/widget.py	Wed Mar 19 20:42:52 2014 +0100
@@ -22,11 +22,11 @@
         #: Theme
         self.theme = default_theme
 
-        ### placing and size
         #: Position inside parent widget. Modified by layout manager.
         self.pos = Point()
         #: Actual size. Modified by layout manager.
         self.size = Size(10, 10)
+
         #: Requested size. Layout manager will use this when placing the widget.
         self.sizereq = Size(1, 1)
         #: Minimal size of widget. Widget will never be sized smaller than this.
@@ -35,7 +35,7 @@
         #: None means no maximum size (infinite).
         self.sizemax = Size(None, None)
 
-    ## placing and size ##
+    ## position and size ##
 
     @property
     def x(self):
@@ -53,10 +53,24 @@
     def height(self):
         return self.size.h
 
-    ## drawing ##
+    ## appearance ##
 
-    def draw(self, buffer, x, y):
+    def draw(self, buffer, x=0, y=0):
         pass
 
     def set_theme(self, theme):
         self.theme = theme
+
+    ## timeouts ##
+
+    def add_timeout(self, delay, callback, *args):
+        """Register `callback` to be called after `delay` seconds."""
+        self.parent.add_timeout(self, delay, callback, *args)
+
+    def remove_timeout(self, callback, *args):
+        """Unregister callback previously registered with add_timeout.
+
+        Removes all timeouts with the `callback` and `args`.
+
+        """
+        self.parent.remove_timeout(self, callback, *args)
--- a/tuikit/core/window.py	Wed Mar 19 00:32:38 2014 +0100
+++ b/tuikit/core/window.py	Wed Mar 19 20:42:52 2014 +0100
@@ -1,8 +1,6 @@
 from tuikit.core.buffer import Buffer
 from tuikit.core.signal import Signal
 from tuikit.core.container import Container
-from tuikit.core.coords import Point
-from tuikit.core.theme import default_theme
 
 
 class Window(Container):
@@ -17,40 +15,22 @@
         """New buffer for the window will be created unless given existing
         `buffer` as parameter."""
         Container.__init__(self)
-        self.pos = Point()
         self._buffer = None
-        self.sig_resized = Signal()
         self.buffer = buffer or Buffer()
 
     @property
-    def x(self):
-        return self.pos.x
-
-    @property
-    def y(self):
-        return self.pos.y
-
-    @property
     def buffer(self):
         return self._buffer
 
     @buffer.setter
     def buffer(self, buffer):
-        # disconnect signals from old buffer
-        if self._buffer:
-            self.sig_resized.disconnect(self._buffer.resize)
         # replace the buffer
         self._buffer = buffer
-        # resize buffer when window gets resized
-        self.sig_resized.connect(buffer.resize)
-
-    @property
-    def size(self):
-        return self.buffer.size.readonly()
+        self.size = buffer.size
 
     def resize(self, w, h):
+        """Resize buffer when window gets resized."""
         self.buffer.resize(w, h)
-        self.sig_resized(w, h)
         self.redraw()
 
     def redraw(self):
@@ -63,29 +43,14 @@
         buffer.draw(self.buffer, x, y)
 
 
-class WindowManager:
-
-    def __init__(self, theme=default_theme):
-        self.windows = []
-        self.theme = theme
+class WindowManager(Container):
 
-    def add(self, window):
-        self.windows.append(window)
-        window.set_theme(self.theme)
-
-    def set_theme(self, theme):
-        self.theme = theme
-        for window in self.windows:
-            window.set_theme(theme)
+    def __init__(self, timer):
+        Container.__init__(self)
+        self.timer = timer
 
     def resize(self, w, h):
-        self.windows[0].resize(w, h)
-
-    def draw(self, buffer, x=0, y=0):
-        for window in self.windows:
-            window.draw(buffer,
-                        x + window.x,
-                        y + window.y)
+        self.children[0].resize(w, h)
 
 #    def keypress(self, keyname, char, mod=0):