Mercurial > getan > getan
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() +