#!/usr/bin/env python """pywmdatetime.py WindowMaker dockapp that displays time, date, weekday and week number. Copyright (C) 2003 Kristoffer Erlandsson Licensed under the GNU General Public License. Changes: 2003-09-01 Kristoffer Erlandsson Fixed a bug where the week didn't update if we used %q style week numbering. 2003-06-28 Kristoffer Erlandsson Fixed a bug where a mouse click caused an infinite loop 2003-06-26 Kristoffer Erlandsson Fixed bug when longer strings didn't get cleared when shorter ones where painted. Now only repaint the strings when they have changed. 2003-06-24 Kristoffer Erlandsson Added event handling for graceful shutdown 2003-06-16 Kristoffer Erlandsson First workingish version """ usage = """pywmdatetime.py [options] Available options are: -h, --help asprint this help -f, --foreground set the foreground color -b, --background set the background color -F, --font set the font name -t, --timeformat set the time format -d, --dateformat set the date format -y, --weekdayformat set the weekday format -e, --weekformat set the week format -r, --rgbfile set the rgb file to get color codes from -c, --configfile set the config file to use --debug shows the pixmap The formats are the same as Python's strftime() accept. See the sample rc-file for more information about this. """ import sys import time import getopt import os import wmdocklib width = 64 height = 64 xOffset = 4 yOffset = 4 timeDefaultFormat = '%H:%M:%S' dateDefaultFormat = '%d-%m-%y' dayDefaultFormat = '%A' weekDefaultFormat = 'wk %q' # %q added by Kristoffer for different week calculation. defaultConfigFile = '~/.pywmdatetimerc' maxCharsPerLine = None def addString(s, x, y): try: wmdocklib.addString(s, x, y, xOffset, yOffset, width, height) except ValueError as e: sys.stderr.write('Error when painting string:\n' + str(e) + '\n') sys.stderr.write('test %s' % ((s, x, y, xOffset, yOffset, width, height),)) raise sys.exit(3) def addTimeString(s, x, y): for c in s: charW = 7 charX = (ord(c) - ord('0')) * 7 charY = 64 if not c.isdigit(): charX = 70 charW = 3 wmdocklib.copyXPMArea(charX, charY, charW, 10, x+xOffset+1, y+yOffset) x += charW def clearLine(y): '''Clear a line of text at position y.''' wmdocklib.copyXPMArea(73, yOffset, width - 2 * xOffset, char_height, xOffset, y + yOffset) def getCenterStartPos(s): return wmdocklib.getCenterStartPos(s, width, xOffset) def getVertSpacing(numLines, margin): return wmdocklib.getVertSpacing(numLines, margin-1, height, yOffset) + 1 def calculateWeek(localTime): """Calculate the week number as we do, for example in Sweden. That is, add one to the %W format if the year didn't start on a monday.""" day = int(time.strftime('%j', localTime)) weekDay = int(time.strftime('%w')) - 1 if weekDay == -1: weekDay = 6 lastMonday = day - weekDay if lastMonday % 7 == 0: return int(time.strftime('%W')) return int(time.strftime('%W')) + 1 def parseCommandLine(argv): """Parse the commandline. Return a dictionary with options and values.""" shorts = 'hf:b:t:d:e:y:r:c:F:a' longs = ['antialiased', 'help', 'foreground=', 'background=', 'timeformat=', 'dateformat=', 'debug', 'weekdayformat=', 'weekformat=', 'rgbfile=', 'configfile=', 'font='] try: opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) except getopt.GetoptError as e: sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n') sys.stderr.write(usage) sys.exit(2) d = {} for o, a in opts: if o in ('-h', '--help'): sys.stdout.write(usage) sys.exit(0) if o in ('-a', '--antialiased'): d['antialiased'] = True if o in ('-f', '--foreground'): d['foreground'] = a if o in ('-F', '--font'): d['font'] = a if o in ('-b', '--background'): d['background'] = a if o in ('-t', '--timeformat'): d['timeformat'] = a if o in ('-d', '--dateformat'): d['dateformat'] = a if o in ('-y', '--weekdayformat'): d['weekdayformat'] = a if o in ('-e', '--weekformat'): d['weekformat'] = a if o in ('-r', '--rgbfile'): d['rgbfile'] = a if o in ('-c', '--configfile'): d['configfile'] = a if o in ('--debug'): d['debug'] = True return d def checkForEvents(): event = wmdocklib.getEvent() while not event is None: if event['type'] == 'destroynotify': sys.exit(0) event = wmdocklib.getEvent() def mainLoop(timeFmt, dateFmt, dayFmt, weekFmt): recalcWeek = weekFmt.find('%q') + 1 # True if we found %q. newWeekFmt = weekFmt counter = -1 lastStrs = [''] * 4 while 1: counter += 1 checkForEvents() lt = time.localtime() timeStr = time.strftime(timeFmt, lt)[:maxCharsPerLine] timeX = 3 if antialiased: margin = 6 spacing = getVertSpacing(4, margin) if lastStrs[0] != timeStr: addTimeString(timeStr, timeX, margin-4) margin += 3 else: margin = 4 spacing = getVertSpacing(4, margin) if lastStrs[0] != timeStr: addString(timeStr, timeX, margin) lastStrs[0] = timeStr if counter % 100 == 0: # We only perform the date/week checks/updates once every 100th # iteration. We will maybe lag behind a couple of seconds when # switching, but switching occurs seldom and it will be alot of # unnecessary checks :). dateStr = time.strftime(dateFmt, lt)[:maxCharsPerLine] if recalcWeek: week = calculateWeek(lt) newWeekFmt = weekFmt.replace('%q', str(week)) weekStr = time.strftime(newWeekFmt, lt)[:maxCharsPerLine] dayStr = time.strftime(dayFmt, lt)[:maxCharsPerLine] dateX = getCenterStartPos(dateStr) weekX = getCenterStartPos(weekStr) dayX = getCenterStartPos(dayStr) if lastStrs[1] != dateStr: clearLine(margin + spacing + char_width) addString(dateStr, dateX, margin + spacing + char_width) lastStrs[1] = dateStr if lastStrs[2] != dayStr: clearLine(margin + 2 * (spacing + char_width)) addString(dayStr, dayX, margin + 2 * (spacing + char_width)) lastStrs[2] = dayStr if lastStrs[3] != weekStr: clearLine(margin + 3 * (spacing + char_width)) addString(weekStr, weekX, margin + 3 * (spacing + char_width)) lastStrs[3] = weekStr if counter == 999999: counter = -1 wmdocklib.redraw() time.sleep(0.1) patterns = [ ".+@@+.....#@...#@@#...#@@#....$@%...@@@@+..+=@%..+@@@@@..%@@+...&@@#.....", "$@==@$...+@@..&@--@&.*@--@&...#@%..*@-%%*.$==-@&.*%%%@=.#@-=@*.*@=-@&....", "&@**@&..#@@@..&@$.@%.&-..@%..*@@%..+@+....+@*.%*....+@*.%@.+@+.%@$.-%.*+.", "%@..@%..#+%@.....$@%....+@#..--@%..&@=@#..%@$+$.....--..&@&%@$.%@..%@.%@.", "%@..@%....%@.....%@*...%@-..*@$@%..%@%=@*.%@=@@*...*@&...=@@#..&@%&@@.*+.", "%@..@%....%@....+@%....&-@+.=#.@%...*.$@%.%@%&@-...#@...#@&%@*..=@@@@....", "%@..@%....%@...+@=$......-@.@-%@=&.....@%.%@..%@...=-...@%..@%...+*%@.&%.", "&@**@&....%@..$@=$...&-..-@.@@@@@%.--.*@#.+@$.-@...@%...@-.$@%.$#*.=%.%@.", "$@==@$....%@..#@-%%&.+@--@#....@%..%@-=@$.$@=-@#..+@+...#@-=@*.$@=-@+....", ".+@@+.....%@..@@@@@%..#@@#.....@%..$-@=+...+=@%$..+@+...$#@@&...+@@#.....", ] palette = { ".":"#181818", "+":"#6E6E0F", "@":"#FFFF00", "#":"#A0A009", "$":"#3B3B14", "%":"#B9B907", "&":"#87870C", "*":"#545411", "=":"#E6E602", "-":"#CFCF04", } def main(): clConfig = parseCommandLine(sys.argv) configFile = clConfig.get('configfile', defaultConfigFile) configFile = os.path.expanduser(configFile) fileConfig = wmdocklib.readConfigFile(configFile, sys.stderr) # Merge the two configs, let the commandline options overwrite those in the # configuration file. config = fileConfig for i in clConfig.items(): config[i[0]] = i[1] timeFmt = config.get('timeformat', timeDefaultFormat) dateFmt = config.get('dateformat', dateDefaultFormat) dayFmt = config.get('weekdayformat', dayDefaultFormat) weekFmt = config.get('weekformat', weekDefaultFormat) # openXwindow sets the window title to the program name. If we get the # program name with a path, split it so we only name the window with the # filename. try: programName = sys.argv[0].split(os.sep)[-1] except IndexError: # Should only happen when using the interpreter. programName = '' sys.argv[0] = programName palette[0] = clConfig.get('background', 'black') palette[2] = clConfig.get('foreground', 'cyan3') font = clConfig.get('font', '6x8orig') if clConfig.get('antialiased'): background = [((6,3),(57,19)), ((3,22),(60,60))] else: background = [((3,3),(59,60))] debug = clConfig.get('debug') global char_width, char_height, maxCharsPerLine, antialiased char_width, char_height = wmdocklib.initPixmap(patterns=patterns, font_name=font, bg=0, fg=2, palette=palette, background=background, debug=debug) maxCharsPerLine = (width-2*xOffset) // char_width antialiased = clConfig.get('antialiased', False) wmdocklib.openXwindow(sys.argv, width, height) mainLoop(timeFmt, dateFmt, dayFmt, weekFmt) if __name__ == '__main__': main()