From www.AA6E.net
Revision as of 16:38, 4 July 2010 by Martin (talk | contribs) (Created page with '<pre>#!/usr/bin/env python # -*- coding: utf-8 -*- # PyHamClock version 0.2, 12/6/2009 # PyHamClock displays UTC date and time, along with your local time. # Copyright (C) …')

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# PyHamClock version 0.2, 12/6/2009

#   PyHamClock displays UTC date and time, along with your local time.
#   Copyright (C) 2009 Martin S. Ewing, AA6E
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Developed with Python 2.6.2 on Ubuntu Linux 9.04.  Also tested on Windows XP & 7.

import wx, sys, time, ConfigParser, os

BG1 = wx.Colour(0,0,0)
FG2 = wx.Colour(0,130,0)        # dim green
FG1 = wx.Colour(255, 128, 0)    # orange
INTERVAL = 200                  # msec timer period

# Performance check:  On Intel Core 7-920 with INTERVAL=200, CPU load (one thread)
# is 0.12% over 4 min., after python startup.  Atom 230 -> 0.4%

# We support 3 number size formats, using wxIDs from menu radio buttons.
F_SMALL =  1000
F_MEDIUM = 1001
F_LARGE =  1002
# These are the actual font sizes to use for each format.
F_SIZE = { F_SMALL:14, F_MEDIUM:18, F_LARGE:22 }
# Frame sizes could / should be calculated from the font sizes, but it is
# easier to wire them in for now...
FRAME_SIZE = { F_SMALL:(192,50), F_MEDIUM:(234,62), F_LARGE:(282,74) }

CFG_SECTION = 'pyhamclock'
CFG_DEFAULTS = { 'hpos': 0, 'vpos': 0, 'format': F_MEDIUM }
CFG_FILE = 'config.conf'

# Get human-readable time strings
def capture():
    time_now = time.time()                  # use same instant for both conversions
    myformat = "%m/%d %H:%M:%S"   # mm/dd HH:MM:SS
    utc_s   = time.strftime(myformat, time.gmtime(time_now))    
    local_s = time.strftime(myformat, time.localtime(time_now))
    return utc_s, local_s

# ---- Config. file handler (for window position & size)
# file name: $HOME/.config/pyhamclock/config.conf
# File format:
# [pyhamclock]
# hpos: nnn     # initial frame position (h)
# vpos: nnn     # initial frame position (v)
# format: n     # format (F_SMALL, F_MEDIUM, F_LARGE)

class PHC_configurator(object):
    
    def __init__(self):
        cfgdpath = os.path.expanduser('~/.config/pyhamclock')
        if not os.access(cfgdpath, os.W_OK):
            os.makedirs(cfgdpath, 0744)             # make dir if needed
        self.cfgpath = os.path.join(cfgdpath,CFG_FILE)
        self.config = ConfigParser.RawConfigParser(CFG_DEFAULTS)
        # Does config.conf file exist & readable?   if not, create it with defaults.
        if not os.access(self.cfgpath, os.R_OK):
            self.config.add_section(CFG_SECTION)
            for x in CFG_DEFAULTS:
                self.config.set(CFG_SECTION, x, CFG_DEFAULTS[x])
            with open(self.cfgpath, 'wb') as cfgfile:
                self.config.write(cfgfile)
        else:
            self.config.read(self.cfgpath)
        return None

    def getpos(self):
        hp = self.config.getint(CFG_SECTION, 'hpos')
        vp = self.config.getint(CFG_SECTION, 'vpos')
        return (hp,vp)

    def getformat(self):
        return self.config.getint(CFG_SECTION, 'format')

    def setpos(self, pos):
        self.config.set(CFG_SECTION, 'hpos', pos[0])
        self.config.set(CFG_SECTION, 'vpos', pos[1])

    def setformat(self, fmt):
        self.config.set(CFG_SECTION, 'format', fmt)

    def write(self):
        with open(self.cfgpath, 'wb') as cfgfile:
            self.config.write(cfgfile)

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = \
            wx.MINIMIZE_BOX|wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.RESIZE_BORDER
        wx.Frame.__init__(self, *args, **kwds)
        
        self.menubar = wx.MenuBar()
        self.f_menu = wx.Menu()
        self.f_menu.AppendRadioItem(F_SMALL, "Small")
        self.f_menu.AppendRadioItem(F_MEDIUM, "Medium")
        self.f_menu.AppendRadioItem(F_LARGE, "Large")
        self.f_menu.AppendSeparator()
        quitter = self.f_menu.Append(-1, "Quit", "", wx.ITEM_NORMAL)
        self.menubar.Append(self.f_menu, "File")
        tmp_menu = wx.Menu()
        abouter = tmp_menu.Append(-1, "About", "", wx.ITEM_NORMAL)
        self.menubar.Append(tmp_menu, "Help")
        self.SetMenuBar(self.menubar)

        self.cfg = PHC_configurator()   # check $HOME/.config/pyhamclock/config.conf
        posreq = self.cfg.getpos()      # position requested by conf file
        scrnsize = wx.GetDisplaySize()  # ensure clock is on screen
        posclip = (min(posreq[0], scrnsize[0] - 50), 
                    min(posreq[1], scrnsize[1] - 50))
        self.SetPosition(posclip)
        # Note that self.format is the wxID of its corresponding menu radio button.
        f = self.cfg.getformat()
        self.format = max(F_SMALL,min(F_LARGE,f))
        # set the corresponding radio button in menu
        for x in F_SIZE:
            self.f_menu.Check(x, self.format == x)
            
        self.SetTitle("PyHamClock")
#        self.SetToolTipString("PyHamClock!")

        self.Bind(wx.EVT_MENU, self.DoFormat, id=1000, id2=1002)
        self.Bind(wx.EVT_MENU, self.DoQuit, quitter)
        self.Bind(wx.EVT_MENU, self.DoAbout, abouter)
        self.Bind(wx.EVT_CLOSE, self.DoQuit)    # (event from close box)

        self.label_utc = wx.StaticText(self, -1, "UTC")
        self.label_utc.SetForegroundColour(FG2)
        self.label_local = wx.StaticText(self, -1, "Local")
        self.label_local.SetForegroundColour(FG2)

        t_utc, t_loc = capture()        # Get current times
        
        self.text_ctrl_utc = wx.StaticText(self,-1, t_utc,
            style=wx.ALIGN_LEFT)
        self.text_ctrl_utc.SetBackgroundColour(BG1)
        self.text_ctrl_utc.SetForegroundColour(FG1)
        self.text_ctrl_local = wx.StaticText(self, -1, t_loc,
            style=wx.ALIGN_LEFT)
        self.text_ctrl_local.SetBackgroundColour(BG1)
        self.text_ctrl_local.SetForegroundColour(FG1)

        self.__do_layout()
       
        # Set up interval timer
        self.old_time = int(time.time())
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.timer.Start(INTERVAL) # continuous

    def __do_layout(self):
        self.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, 0, ""))
        self.SetBackgroundColour(BG1)
        self.SetForegroundColour(FG1)
        time_font = wx.Font(F_SIZE[self.format], wx.SWISS, wx.NORMAL, wx.NORMAL, 0, "")
        self.text_ctrl_utc.SetFont(time_font)
        self.text_ctrl_local.SetFont(time_font)
        self.SetClientSize(FRAME_SIZE[self.format])
        
        sizer_1 = wx.FlexGridSizer(rows=2, cols=2, hgap=5, vgap=0)
        sizer_1.AddGrowableCol(1)
        sizer_1.Add(self.label_utc, border=4, flag=wx.LEFT|wx.TOP)
        sizer_1.Add(self.text_ctrl_utc)
        sizer_1.Add(self.label_local, border=4, flag=wx.LEFT|wx.TOP)
        sizer_1.Add(self.text_ctrl_local)
        self.SetSizer(sizer_1)
        self.Layout()

    def DoFormat(self, event):      # Find which format item is now checked.
        for x in F_SIZE:
            if self.f_menu.IsChecked(x):
                self.format = x     # requested format option
        self.__do_layout()          # rearrange based on chosen format
        return

    def OnTimer(self, event):       # The timer INTERVAL should be <= abt 250 ms
                                    # to keep well synced with UTC seconds transition.
        int_time = int(time.time()) # int. seconds
        if int_time >= self.old_time + 1:   # on most interrupts, nothing happens
            self.old_time = int_time        # but we will update display 
            utc, loc = capture()            # when we pass a 1 sec boundary
            self.text_ctrl_utc.SetLabel(utc)
            self.text_ctrl_local.SetLabel(loc)
            event.Skip()
        event.Skip()
    
    def DoQuit(self, event):
        # Save our .config info
        self.cfg.setpos(self.GetPosition())
        self.cfg.setformat(self.format)
        self.cfg.write()
        sys.exit()

    def DoAbout(self, event):
        wx.MessageBox(
"""PyHamClock v 0.2
(c) 2009 Martin Ewing AA6E
http://aa6e.net, aa6e@arrl.net""")
        event.Skip()

# end of class MyFrame

if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame_pyhamclock = MyFrame(None, -1, "")
    app.SetTopWindow(frame_pyhamclock)
    frame_pyhamclock.Show()
    app.MainLoop()