###################################################################### # Copyright (c) 2007, Petteri Aimonen # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice and this list of conditions. # * Redistributions in binary form must reproduce the above copyright # notice and this list of conditions in the documentation and/or other # materials provided with the distribution. '''Miscellanious utility functions''' import traceback import datetime import wx import sys import settings import gettext import os.path class Translator: '''Keep track of current language''' def __init__(self): self.catalog = gettext.NullTranslations() def _(self, string): """Translate message""" return self.catalog.gettext(string).decode('UTF-8') def setlanguage(self, code): """Set language. Code is eg. 'fi'""" locales = {'fi': 'fi_FI.UTF-8', 'en': 'en_EN.UTF-8'} langs = {'fi': wx.LANGUAGE_FINNISH, 'en': wx.LANGUAGE_ENGLISH} mos = {'fi': 'finnish.mo', 'en': None} if not locales.has_key(code) or not langs.has_key(code): p = (code, ', '.join(locales.keys())) sys.stderr.write("Invalid language code: %s (supported %s)\n" % p) code = 'en' wx.Locale(langs[code], wx.LOCALE_LOAD_DEFAULT) basedir = settings.parser.get('DEFAULT', 'basedir') if mos[code]: langfile = open(os.path.join(basedir, 'locale', mos[code]), 'rb') self.catalog = gettext.GNUTranslations(langfile) translator = Translator() _ = translator._ def recode(string): '''Convert string to unicode. Try UTF-8, and if it fails, remove unknown chars''' try: return unicode(string).decode('UTF-8') except UnicodeDecodeError: return unicode(string, errors = 'ignore') def unicode_traceback(): '''Unicode compatible traceback. Converts system messages etc. to unicode from locale charset. Tries decoding from utf-8, and if that fails, strips unknown chars ''' msg = recode(traceback.format_exc()) sys.stderr.write(msg) return msg def lineends(): '''Return platform-specific line end sequence''' if sys.platform.startswith('linux'): return '\n' else: return '\r\n' def tupleslices(list, size): '''Generates a list of tuples of a specific size. For example: tupleslices([1,2,3,4], 2) returns [(1,2), (2,3), (3,4)] ''' result = [] for i in range(len(list) - size + 1): t = tuple(list[i:(i + size)]) result.append(t) return result def date_towx(date): '''Convert python datetime.date to wx.DateTime''' assert isinstance(date, datetime.date) r = wx.DateTime() r.ParseDate(date.strftime("%Y-%m-%d")) return r def date_fromwx(date): '''Convert wx.DateTime to python datetime.date''' s = date.FormatISODate() year = int(s[0:4]) month = int(s[5:7]) day = int(s[8:10]) return datetime.date(year, month, day) class TreeNode: '''A simple element tree''' def __init__(self, parent = None, userdata = None): self.parent = parent if parent: self.parent.children.append(self) self.children = [] self.userdata = userdata def __repr__(self): return "TreeNode(%s, %s)" % (repr(self.parent), repr(self.userdata)) def __cmp__(self, other): return cmp(self.userdata, other.userdata) def __contains__(self, other): '''Return True if other is in this subtree. Compare objects, not userdata. ''' if self is other: return True for child in self.children: if other in child: return True return False def get_sibling(self, delta): '''Get sibling of this node. Delta = -1 for previous, delta = 1 for next. Return None if this is the last/first child. ''' if self.parent is None: return None index = self.parent.children.index(self) + delta if index > len(self.parent.children) - 1 or index < 0: return None else: return self.parent.children[index] def get_gslist(self): '''Get list of global siblings of this node, in order. A global sibling is node that is on the same level, but not necessarily under the same parent node.''' if self.parent is None: return [self] # Parent node has no siblings gs = [] for parent in self.parent.get_gslist(): gs += parent.children return gs def get_globalsibling(self, delta): '''Get a global sibling of this node.''' gs = self.get_gslist() index = gs.index(self) + delta if index > len(gs) - 1 or index < 0: return None else: return gs[index] def traverse_tree(self, function, max_depth = None): '''Execute function for each node in the (sub)tree. Visit root before children. Max_depth is recursion limit, max_depth=0 means only visit root. ''' function(self) if max_depth is not None: max_depth -= 1 if max_depth < 0: return for child in self.children: child.traverse_tree(function, max_depth) def find_node(self, userdata): '''Find node with specified userdata. Return None if not found.''' if self.userdata == userdata: return self for child in self.children: r = child.find_node(userdata) if r: return r return None if __name__ == '__main__': print "Unit testing" assert tupleslices(range(5), 2) == [(0,1), (1,2), (2,3), (3,4)] assert tupleslices(range(5), 4) == [(0,1,2,3), (1,2,3,4)] root = TreeNode() root.userdata = [] def addchild(node): for i in range(10): TreeNode(node, node.userdata + [i]) root.traverse_tree(addchild, 1) assert root.children[0].userdata == [0] assert root.children[0].children[5].userdata == [0, 5] assert root.children[5].get_sibling(-1) is root.children[4] assert root.children[0].get_sibling(-1) is None assert (root.children[5].children[3].get_sibling(1) is root.children[5].children[4]) assert root.children[9].get_sibling(1) is None assert root.children[4].children[3] in root assert root.children[3] in root assert root in root assert root.children[4] not in root.children[3] assert root.children[3].children[2] not in root.children[2] assert (root.children[3].children[9].get_globalsibling(1) is root.children[4].children[0]) assert root.children[0].children[0].get_globalsibling(-1) is None root.traverse_tree(lambda n: n.children.sort(reverse = True)) assert root.children[3].children[0].userdata == [9 - 3, 9 - 0] assert root.find_node([1,1]).userdata == [1,1] assert (date_fromwx(date_towx(datetime.date.today())) == datetime.date.today())