Add TableView plus demo.
authorRadek Brich <radek.brich@devl.cz>
Sat, 08 Oct 2011 17:16:07 +0200
changeset 21 8553a6bd2d82
parent 20 472a753664f9
child 22 6ca8b2d221c3
Add TableView plus demo.
demo_tableview.py
docs/index.rst
docs/tableview.rst
tuikit/__init__.py
tuikit/application.py
tuikit/backend_curses.py
tuikit/tableview.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo_tableview.py	Sat Oct 08 17:16:07 2011 +0200
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import locale
+locale.setlocale(locale.LC_ALL, '')
+
+from tuikit import Application
+from tuikit.layout import VerticalLayout
+from tuikit.tableview import TableView, TableModel
+
+
+class MyApplication(Application):
+    def __init__(self):
+        Application.__init__(self)
+        self.top.connect('keypress', self.globalkeypress)
+
+        data = []
+        for y in range(100):
+            row = [str(y)]
+            for x in range(10):
+                row.append('r{}:c{}'.format(y, x))
+            data.append(row)
+        model = TableModel(data)
+        
+        view = TableView(model)
+        view.addcolumn(header=True, expand=False, sizereq=5)
+        for x in range(10):
+            view.addcolumn(title='head'+str(x))
+        
+        self.top.add(view, expand=True, fill=True)
+
+        vert = VerticalLayout()
+        self.top.layout(vert)
+
+    def globalkeypress(self, keyname, char):
+        if keyname == 'escape':
+            self.terminate()
+
+
+if __name__ == '__main__':
+    app = MyApplication()
+    app.start()
+
--- a/docs/index.rst	Fri Oct 07 12:36:14 2011 +0200
+++ b/docs/index.rst	Sat Oct 08 17:16:07 2011 +0200
@@ -17,6 +17,7 @@
    focus
    redraw
    colors
+   tableview
 
 .. inheritance-diagram:: tuikit.application
                          tuikit.eventsource
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/tableview.rst	Sat Oct 08 17:16:07 2011 +0200
@@ -0,0 +1,11 @@
+TableView
+=========
+
+.. toctree::
+   :maxdepth: 3
+   :titlesonly:
+
+.. automodule:: tuikit.tableview
+   :members:
+   :show-inheritance:
+
--- a/tuikit/__init__.py	Fri Oct 07 12:36:14 2011 +0200
+++ b/tuikit/__init__.py	Sat Oct 08 17:16:07 2011 +0200
@@ -13,6 +13,7 @@
 from tuikit.menubar import MenuBar
 from tuikit.pager import Pager
 from tuikit.scrollbar import VScrollbar
+from tuikit.tableview import TableModel, TableView
 from tuikit.textedit import TextEdit
 from tuikit.treeview import TreeNode, TreeModel, TreeView
 from tuikit.widget import Widget
--- a/tuikit/application.py	Fri Oct 07 12:36:14 2011 +0200
+++ b/tuikit/application.py	Sat Oct 08 17:16:07 2011 +0200
@@ -137,6 +137,7 @@
     def applytheme(self):
         screen = self.screen
         screen.setcolor('normal',                  'white on black')
+        screen.setcolor('strong',                  'white on black, bold')
         screen.setcolor('active',                  'black on cyan')
         screen.setcolor('window:normal',           'white on blue')
         screen.setcolor('window:controls',         'white on blue, bold')
--- a/tuikit/backend_curses.py	Fri Oct 07 12:36:14 2011 +0200
+++ b/tuikit/backend_curses.py	Sat Oct 08 17:16:07 2011 +0200
@@ -513,7 +513,7 @@
             return [('keypress', 'Unknown', None)]
 
         if keyname == 'mouse':
-           return self.process_xterm_mouse()
+            return self.process_xterm_mouse()
 
         return [('keypress', keyname, None)]
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/tableview.py	Sat Oct 08 17:16:07 2011 +0200
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+
+import math
+
+from tuikit.widget import EventSource, Widget
+from tuikit.common import Coords
+
+
+class TableModel(EventSource):
+    def __init__(self, list_of_lists):
+        EventSource.__init__(self)
+        self.addevents('change')
+        self.data = list_of_lists
+    
+    def getcount(self):
+        '''Get number of rows.'''
+        return len(self.data)
+    
+    def getrows(self, begin, end):
+        '''Get rows from begin to end, including begin, excluding end.'''
+        return self.data[begin:end]
+    
+    def update(self, row, col, val):
+        self.data[row][col] = val
+
+
+class Column:
+    
+    '''Columns description.'''
+    
+    def __init__(self, title='', expand=True, sizereq=1,
+        header=False, readonly=False, maxlength=None):
+        '''Create column with default values.'''
+        
+        self.title = title
+        '''Column title'''
+        
+        self.expand = expand
+        '''Use free space?'''
+        
+        self.sizereq = sizereq
+        '''Size request. Meaning depends on value of expand:
+        
+        When false, sizereq is number of characters.
+        When true, sizereq is relative size ratio.
+        
+        '''
+        
+        self.size = 0
+        '''Computed size of column.'''
+        
+        self.header = header
+        '''Is this header column? (bold, readonly)'''
+        
+        self.readonly = readonly
+        '''Allow edit?'''
+        
+        self.maxlength = maxlength
+        '''Maximum length of value (for EditField).'''
+
+
+class TableView(Widget):
+    def __init__(self, model=None, width=20, height=20):
+        Widget.__init__(self, width, height)
+        
+        # model
+        self._model = None
+        self.setmodel(model)
+        
+        self.columns = []
+        self.spacing = 1
+        
+        self.offset = Coords()
+        
+        self.connect('draw', self.on_draw)
+        self.connect('keypress', self.on_keypress)
+    
+    def getmodel(self):
+        return self._model
+    
+    def setmodel(self, value):
+        if self._model:
+            self._model.disconnect('change', self.redraw)
+        self._model = value
+        if self._model:
+            self._model.connect('change', self.redraw)
+    
+    model = property(getmodel, setmodel)
+    
+    def addcolumn(self, *args, **kwargs):
+        for col in args:
+            self.columns.append(col)
+        if len(args) == 0:
+            col = Column(**kwargs)
+            self.columns.append(col)
+    
+    def compute_column_sizes(self):
+        total_space = self.size.w - self.spacing * len(self.columns)
+        no_expand_cols = [col for col in self.columns if not col.expand]
+        no_expand_size = sum([col.sizereq for col in no_expand_cols])
+        expand_cols = [col for col in self.columns if col.expand]
+        expand_num = len(expand_cols)
+        expand_size = total_space - no_expand_size
+        
+        # compute size of cols without expand
+        for col in no_expand_cols:
+            col.size = col.sizereq
+            
+        # compute size of cols with expand
+        if no_expand_size > total_space + expand_num:
+            for col in expand_cols:
+                col.size = 1
+        else:
+            total_req = sum([col.sizereq for col in expand_cols])
+            remaining_space = 0.
+            for col in expand_cols:
+                frac, intp = math.modf(expand_size * col.sizereq / total_req)
+                col.size = int(intp)
+                remaining_space += frac
+                if remaining_space > 0.99:
+                    remaining_space -= 1.
+                    col.size += 1
+    
+    def draw_head(self, screen, x, y):
+        screen.pushcolor('strong')
+        for col in self.columns:
+            screen.puts(x, y, col.title[:col.size])
+            x += col.size + self.spacing
+        screen.popcolor()
+    
+    def draw_row(self, screen, x, y, row):
+        for col, data in zip(self.columns, row):
+            if col.header:
+                screen.pushcolor('strong')
+            screen.puts(x, y, data[:col.size])
+            if col.header:
+                screen.popcolor()
+            x += col.size + self.spacing
+    
+    def on_draw(self, screen, x, y):
+        screen.pushcolor('normal') 
+        head_size = 1
+        numrows = min(self.model.getcount() - self.offset.y, self.size.h - head_size)
+        rows = self.model.getrows(self.offset.y, self.offset.y + numrows)
+        self.compute_column_sizes()
+        self.draw_head(screen, x, y)
+        y += head_size
+        for row in rows:
+            self.draw_row(screen, x, y, row)
+            y += 1
+        screen.popcolor()        
+
+    def on_keypress(self, keyname, char):
+        if keyname:
+            if keyname == 'up':    self.move_up()
+            if keyname == 'down':  self.move_down()
+            if keyname == 'left':  self.move_left()
+            if keyname == 'right': self.move_right()
+        self.redraw()
+