diff getan/view.py @ 23:9c4e8ba3c4fa

Added a new implementation of 'getan' based on urwid, a python console user interface library.
author Ingo Weinzierl <ingo_weinzierl@web.de>
date Sat, 28 Aug 2010 20:16:58 +0200
parents
children c89721a3f0f8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/getan/view.py	Sat Aug 28 20:16:58 2010 +0200
@@ -0,0 +1,368 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# (c) 2010 by Ingo Weinzierl <ingo.weinzierl@intevation.de>
+#
+# This is Free Software licensed under the terms of GPLv3 or later.
+# For details see LICENSE coming with the source of 'getan'.
+#
+
+import logging
+
+import urwid
+import urwid.raw_display
+
+from getan.states import *
+from getan.utils  import short_time, format_datetime, format_time
+
+APP_TITLE          = u" .: getan next generation :."
+APP_REALLY_QUIT    = u" Wollen Sie wirklich beenden? "
+APP_CHOOSE_PROJECT = u" Wählen Sie ein Projekt: "
+
+PROJECTS_TITLE = u" Liste registrierter Projekte"
+ENTRIES_TITLE  = u" Liste der letzten Einträge"
+
+logger = logging.getLogger()
+
+class ListWidget(urwid.BoxWidget):
+    def _update_view(self):
+        logger.debug("ListWidget: update view now.")
+        self.frame.set_body(self.body)
+        self.frame.set_footer(self.footer)
+        self.frame.set_header(self.header)
+
+    def update_focus(self, focus, unfocus=-1):
+        logger.debug("ListWidget: focus row (index = %d)" % focus)
+        logger.debug("ListWidget: unfocus row (index = %d)" % unfocus)
+        if focus >= 0 and focus <= len(self.rows)-1:
+            self.rows[focus].focus = True
+            self.rows[focus].update_w()
+        if unfocus >= 0:
+            self.rows[unfocus].focus = False
+            self.rows[unfocus].update_w()
+
+    def render(self, size, focus=False):
+        maxcol, maxrow = size
+        return self.frame.render((maxcol, maxrow), focus)
+
+    def set_footer_text(self, text, attr, edit=False):
+        if edit:
+            logger.debug("ListWidget: set footer text (edit) = '%s'" % text)
+            self.frame.set_footer(urwid.AttrWrap(urwid.Edit(text),attr))
+        else:
+            logger.debug("ListWidget: set footer text = '%s'" % text)
+            self.frame.set_footer(urwid.AttrWrap(urwid.Text(text),attr))
+
+    def row_count(self):
+        if not self.rows: return 0
+        return len(self.rows)
+
+    def item_in_focus(self):
+        return self.rows[self.focused].get_item()
+
+    def up(self):
+        logger.debug("ListWidget: navigate to upper row.")
+        if self.focused > 0:
+            self.focused = self.focused - 1
+            self.update_focus(self.focused, self.focused+1)
+
+    def down(self):
+        logger.debug("ListWidget: navigate to lower row.")
+        if self.focused < len(self.rows) - 1:
+            self.focused = self.focused + 1
+            self.update_focus(self.focused, self.focused-1)
+
+    def select(self):
+        node = self.rows[self.focused]
+        logger.debug("ListWidget: select row '%s'" % self.focused)
+        node.select()
+        if node.selected:
+            self.selection.append(node)
+        else:
+            if node in self.selection:
+                self.selection.pop()
+        logger.debug("ListWidget: all selected rows: %r" % self.selection)
+        return node
+
+    def clear(self):
+        logger.debug("EntryList: clear focus and selection of all entries.")
+        for node in self.selection:
+            if node.selected: node.select()
+        self.update_focus(-1, self.focused)
+        self.focused = False
+
+
+class ProjectNode(urwid.WidgetWrap):
+    MODES = [
+        (0, 'Gesamt'),
+        (1, 'Jahr'),
+        (2, 'Monat'),
+        (3, 'Woche'),
+        (4, 'Tag')
+    ]
+
+    def __init__(self, proj, mode=3):
+        self.selected   = False
+        self.focus      = False
+        self.mode       = self.MODES[mode]
+        self.item       = proj
+        w               = self.update()
+        self.__super.__init__(w)
+        self.update_w()
+
+    def update(self):
+        logger.debug("Update ProjectNode '%s'" % self.item.desc)
+        time_str    = self._get_formatted_time()
+        description = urwid.Text('%s %s' % (self.item.key, self.item.desc))
+        time        = urwid.Text('%s (%s)' % (self.mode[1], time_str))
+        self.widget = urwid.AttrWrap(urwid.Columns([description, time]),None)
+        self._w     = self.widget
+        self.update_w()
+        return self._w
+
+    def _get_formatted_time(self):
+        return human_time(self._get_time())
+
+    def _get_time(self):
+        if self.mode == self.MODES[0]:
+            return self.item.total
+        if self.mode == self.MODES[1]:
+            return self.item.year()
+        if self.mode == self.MODES[2]:
+            return self.item.month()
+        if self.mode == self.MODES[3]:
+            return self.item.week()
+        if self.mode == self.MODES[4]:
+            return self.item.day()
+        return self.item.week()
+
+    def get_item(self):
+        return self.item
+
+    def switch_time_mode(self):
+        tmp = self.mode[0] + 1
+        if tmp > 4: self.mode = self.MODES[0]
+        else:       self.mode = self.MODES[tmp]
+        self.update()
+
+    def update_w(self):
+        if self.focus:
+            if self.selected:
+                self._w.focus_attr = 'selected focus'
+                self._w.attr       = 'selected focus'
+            else:
+                self._w.focus_attr = 'focus'
+                self._w.attr       = 'focus'
+        else:
+            if self.selected:
+                self._w.focus_attr = 'selected'
+                self._w.attr       = 'selected'
+            else:
+                self._w.focus_attr = 'body'
+                self._w.attr       = 'body'
+
+    def select(self):
+        self.selected = not self.selected
+        self.update_w()
+
+
+class ProjectList(ListWidget):
+    def __init__(self, controller, rows):
+        self.selection    = []
+        self.focused      = 0
+        self.controller   = controller
+        self.raw_projects = rows
+
+        self.head       = urwid.LineBox(
+            urwid.AttrWrap(urwid.Text("\n%s\n" % PROJECTS_TITLE),'project_header'))
+        self.foot      = urwid.Edit()
+        self.rows  = [ProjectNode(x) for x in rows]
+        self.listbox   = urwid.ListBox(urwid.SimpleListWalker(self.rows))
+        self.body      = urwid.LineBox(urwid.Padding(urwid.AttrWrap(
+            self.listbox, 'entries'),('fixed left',1),('fixed right',1)))
+        self.frame     = urwid.Frame(self.body, header=self.head,
+                                    footer=self.foot)
+        self.update_focus(self.focused)
+        self._total_time()
+
+    def _total_time(self):
+        logger.debug("ProjectList: update projects total time.")
+        total = 0
+        for proj in self.rows:
+            total += proj._get_time()
+        self.frame.set_footer(urwid.AttrWrap(
+            urwid.Text(' Alle Projekte: %s %s'
+                       % (proj.mode[1],human_time(total))), 'project_footer'))
+
+    def update(self):
+        logger.debug("ProjectList: update focused project row now.")
+        self.rows[self.focused].update()
+        self._total_time()
+        self.controller.view.loop.draw_screen()
+
+    def update_all(self):
+        logger.debug("ProjectList: update all project rows now.")
+        for proj in self.rows:
+            proj.update()
+        self._total_time()
+        self.controller.view.loop.draw_screen()
+
+    def switch_time_mode(self):
+        logger.debug("ProjectList: switch time mode now.")
+        for proj in self.rows:
+            proj.switch_time_mode()
+        self.controller.view.loop.draw_screen()
+
+    def unhandled_keypress(self, key):
+        logger.debug("ProjectList: unhandled keypress '%r'" % key)
+
+    def select_project(self, project):
+        for proj_node in self.rows:
+            if proj_node.project.key == project.key:
+                idx = self.rows.index(proj_node)
+                self.update_focus(idx, self.focused)
+                self.focused = idx
+                self.select()
+                break
+
+
+class EntryNode(urwid.WidgetWrap):
+    def __init__(self, entry):
+        self.selected = False
+        self.focus    = False
+        self.item     = entry
+        w             = self.update()
+        self.__super.__init__(w)
+        self.update_w()
+
+    def update(self):
+        logger.debug("EntryNode: update entry '%s'." % self.item.desc)
+        row = urwid.Text(' %s [%s] %s' \
+                         % (format_datetime(self.item.start), 
+                            short_time(self.item.duration().seconds),
+                            self.item.desc), wrap='clip')
+        self.widget = urwid.AttrWrap(row, None)
+        self._w     = self.widget
+        self.update_w()
+        return self._w
+
+    def update_w(self):
+        if self.focus:
+            if self.selected:
+                self._w.focus_attr = 'selected focus entry'
+                self._w.attr       = 'selected focus entry'
+            else:
+                self._w.focus_attr = 'focus entry'
+                self._w.attr       = 'focus entry'
+        else:
+            if self.selected:
+                self._w.focus_attr = 'selected entry'
+                self._w.attr       = 'selected entry'
+            else:
+                self._w.focus_attr = 'entry body'
+                self._w.attr       = 'entry body'
+
+    def select(self):
+        logger.debug("EntryNode: update selection of entry '%s'"
+                     % self.item.desc)
+        self.selected = not self.selected
+        self.update_w()
+
+
+class EntryList(ListWidget):
+    def __init__(self, rows):
+        self.selection = []
+        self.focused   = 0
+        self.rows      = [EntryNode(x) for x in rows]
+        listbox        = urwid.ListBox(urwid.SimpleListWalker(self.rows))
+        self.body      = urwid.LineBox(urwid.Padding(urwid.AttrWrap(
+            listbox, 'entry_body'),
+            ('fixed left', 1), ('fixed right', 1)))
+        self.header    = urwid.LineBox(urwid.AttrWrap(urwid.Text(
+            "\n%s\n"
+            % ENTRIES_TITLE),'entry_header'))
+        self.footer    = urwid.AttrWrap(urwid.Text(""), 'entry_footer')
+        self.frame     = urwid.Frame(self.body, header=self.header,
+                                     footer=self.footer)
+
+    def set_rows(self, rows):
+        logger.debug("EntryList: set new entries.")
+        self.rows = [EntryNode(x) for x in rows]
+        listbox      = urwid.LineBox(urwid.ListBox(urwid.SimpleListWalker(
+            self.rows)))
+        self.body    = urwid.AttrWrap(listbox, 'entry_body')
+        self._update_view()
+
+
+class GetanView:
+    palette = [
+        ('header',               'white',  'dark blue'),
+        ('project_header',       'white',  'dark cyan'),
+        ('entry_header',         'white',  'dark cyan'),
+        ('footer',               'yellow', 'dark blue'),
+        ('entry_footer',         'white',  'dark blue'),
+        ('project_footer',       'white',  'dark blue'),
+        ('body',                 'white',  'black'),
+        ('entry body',           'white',  'dark blue'),
+        ('entries',              'white',  'black'),
+        ('entry_body',           'white',  'dark blue'),
+        ('focused entry',        'white',  'dark cyan'),
+        ('selected entry',       'yellow', 'light cyan'),
+        ('selected focus entry', 'yellow', 'dark cyan'),
+        ('info',                 'white',  'dark red'),
+        ('focus',                'white',  'dark blue'),
+        ('selected',             'black',  'light green'),
+        ('selected focus',       'yellow', 'dark cyan'),
+        ('question',             'white',  'dark red'),
+        ('running',              'yellow', 'dark green'),
+        ('paused_running',       'white',  'dark red'),
+    ]
+
+    def __init__(self, controller, proj_list, entr_list):
+        urwid.set_encoding("UTF-8")
+        self.controller = controller
+        self.proj_list  = proj_list
+        self.entr_list  = entr_list
+        self.columns    = urwid.Columns([
+            urwid.Padding(self.proj_list, ('fixed left',0),('fixed right',1)),
+            self.entr_list], 0)
+
+        self.header   = urwid.AttrWrap(urwid.Text('%s\n' % APP_TITLE), 'header')
+        self.footer   = urwid.AttrWrap(urwid.Text(APP_CHOOSE_PROJECT),
+                                       'question')
+        self.col_list = self.columns.widget_list
+        view          = urwid.AttrWrap(self.columns, 'body')
+        self.view     = urwid.Frame(view, header=self.header,footer=self.footer)
+        self.state    = PausedProjectsState(controller, self.proj_list)
+
+    def update_view(self):
+        logger.debug("GetanView: update view now.")
+        view = urwid.AttrWrap(self.columns, 'body')
+        self.view.set_body(view)
+        self.loop.draw_screen()
+
+    def get_frame(self):
+        return self.view
+
+    def set_footer_text(self, text, attr, edit=False):
+        if edit:
+            logger.debug("GetanView: set footer text (edit): '%s'" % text)
+            self.view.set_footer(urwid.AttrWrap(urwid.Edit(text),attr))
+        else:
+            logger.debug("GetanView: set footer text: '%s'" % text)
+            self.view.set_footer(urwid.AttrWrap(urwid.Text(text),attr))
+
+    def run(self):
+        self.loop = urwid.MainLoop(self.view, self.palette,
+                                   screen=urwid.raw_display.Screen(),
+                                   unhandled_input=self.controller.unhandled_keypress,
+                                   input_filter=self.controller.input_filter)
+        self.loop.run()
+
+    def unhandled_keypress(self, k):
+        logger.warn("GetanView: unhandled keypress '%r'" % k)
+
+    def exit(self):
+        logger.info("GetanView: shutdown view.")
+        raise urwid.ExitMainLoop()
+
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)