root / pykoticon / trunk / bin / pykoticon @ 80

Revision 80, 18.2 kB (checked in by jerome, 19 years ago)

Added a call to Refresh()

  • Property svn:keywords set to Id
RevLine 
[47]1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
[48]4# PyKotIcon - Windows System Tray Icon for PyKota
[47]5#
6# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
[53]21# $Id$
[47]22#
23#
24
25import sys
[63]26import os
[47]27import urllib
28import urllib2
[65]29import locale
30import gettext
[47]31
[65]32if sys.platform == "win32" :
33    isWindows = 1
34    try :
35        import win32api
36    except ImportError :   
37        raise ImportError, "Mark Hammond's Win32 Extensions are missing. Please install them."
[76]38    else :   
39        iconsdir = "."
[65]40else :       
41    isWindows = 0
[76]42    iconsdir = "/usr/share/pykoticon"   # TODO : change this
[65]43    import pwd
[57]44   
[58]45try :   
46    import wxPython.wx
47    import wx
[63]48    import wx.grid as gridlib
[58]49    hasWxPython = 1
50except ImportError :   
51    hasWxPython = 0
[75]52    raise ImportError, "wxPython is missing. Please install it."
[58]53   
[77]54#DUMPYKOTA_URL = "http://cgi.librelogiciel.com/cgi-bin/dumpykota.cgi"
55DUMPYKOTA_URL = "http://servmediup.unice.fr/cgi-bin/dumpykota.cgi"
[47]56
[65]57def getCurrentUserName() :
58    """Retrieves the current user's name."""
59    if isWindows :
60        return win32api.GetUserName()
61    else :   
62        try :
63            return pwd.getpwuid(os.geteuid())[0]
64        except :
65            return "** Unknown **"
66       
67class Printer :
68    """A class for PyKota Printers."""
69    def __init__(self, printername, priceperpage, priceperjob):
70        """Initialize printer datas."""
71        self.PrinterName = printername
72        try :
73            self.PricePerPage = float(priceperpage)
74        except (ValueError, TypeError) :
75            self.PricePerPage = 0.0
76        try :
77            self.PricePerJob = float(priceperjob)
78        except (ValueError, TypeError) :
79            self.PricePerJob = 0.0
80
[56]81class UserQuota :
82    """A class for PyKota User Print Quota entries."""
83    def __init__(self, printername, pagecount, softlimit, hardlimit, datelimit):
84        """Initialize user print quota datas."""
85        self.PrinterName = printername
86        try :
87            self.PageCounter = int(pagecount)
88        except (ValueError, TypeError) :
89            self.PageCounter = 0
90        try :
91            self.SoftLimit = int(softlimit)
92        except (ValueError, TypeError) :
93            self.SoftLimit = None
94        try :
95            self.HardLimit = int(hardlimit)
96        except (ValueError, TypeError) :
97            self.HardLimit = None
98        self.DateLimit = datelimit
99       
100class User :
101    """A class for PyKota users."""
[66]102    def __init__(self, username, userinfo, userquotas, printers) :
[56]103        """Initialize user datas."""
[65]104        self.UserName = username
[56]105        self.LimitBy = userinfo["limitby"][0].lower()
106        try :
107            self.Balance = float(userinfo["balance"][0])
108        except (ValueError, TypeError) :
109            self.Balance = 0.0
110        try :
111            self.LifeTimePaid = float(userinfo["lifetimepaid"][0])
112        except (ValueError, TypeError) :
113            self.LifeTimePaid = 0.0
[66]114        self.Printers = {}   
115        self.Quotas = {}
[56]116        for i in range(len(userquotas["printername"])) :
117            printername = userquotas["printername"][i]
[66]118            try :
119                pindex = printers["printername"].index(printername)
120            except (ValueError, KeyError) :
121                pass
122            else :   
123                self.Printers[printername] = Printer(printername, \
124                                              printers["priceperpage"][pindex],\
125                                              printers["priceperjob"][pindex])
[56]126            pagecounter = userquotas["pagecounter"][i]
127            softlimit = userquotas["softlimit"][i]
128            hardlimit = userquotas["hardlimit"][i]
129            datelimit = userquotas["datelimit"][i]
[66]130            self.Quotas[printername] = UserQuota(printername, pagecounter, \
131                                                   softlimit, hardlimit, \
132                                                              datelimit)
[56]133           
[55]134class CGINetworkInterface :
135    """A class for all network interactions."""
[56]136    def __init__(self, cgiurl, authname=None, authpw=None) :
[55]137        """Initialize CGI connection datas."""
[56]138        self.cgiurl = cgiurl
139        self.authname = authname
140        self.authpw = authpw
[55]141       
142    def retrieveDatas(self, arguments, fieldnames) :
143        """Retrieve datas from the CGI script."""
144        args = { "report" : 1,
145                 "format" : "csv",
146               } 
147        args.update(arguments)           
148        answer = {}
149        try :           
[56]150            url = "%s?%s" % (self.cgiurl, urllib.urlencode(args))
[55]151            u = urllib2.urlopen(url)
152            lines = u.readlines()
153        except IOError, msg :   
[65]154            raise IOError, _("Unable to retrieve %s : %s") % (url, msg)
[55]155        else :   
156            u.close()
157            try :
158                lines = [ line.strip().split(",") for line in lines ]
159                fields = [field[1:-1] for field in lines[0]]
160                indices = [fields.index(fname) for fname in fieldnames]
161                answer = dict([ (fieldname, \
[56]162                                  [ line[fields.index(fieldname)][1:-1] \
163                                    for line in lines[1:] ]) \
[55]164                                for fieldname in fieldnames ])
165            except :   
[65]166                raise ValueError, _("Invalid datas retrieved from %s") % url
[55]167        return answer
168       
[66]169    def getPrinters(self) :
[55]170        """Retrieve the printer's names."""
171        arguments = { "report" : 1,
172                      "format" : "csv",
173                      "datatype" : "printers",
174                    } 
[66]175        return self.retrieveDatas(arguments, ["printername", "priceperpage", \
176                                              "priceperjob"])
[55]177       
[66]178    def getUser(self, username) :
[55]179        """Retrieve the user's information."""
180        arguments = { "datatype" : "users",
181                      "filter" : "username=%s" % username,
182                    } 
[58]183        return self.retrieveDatas(arguments, ("limitby", "balance", \
184                                                         "lifetimepaid"))
[55]185       
186    def getUserPQuotas(self, username) :
187        """Retrieve the user's print quota information."""
188        arguments = { "datatype" : "upquotas",
189                      "filter" : "username=%s" % username,
190                    } 
[56]191        return self.retrieveDatas(arguments, ("printername", "pagecounter", \
[58]192                                              "softlimit", "hardlimit", \
193                                              "datelimit"))
[56]194                                             
[66]195    def getUserInfo(self, username) :
[56]196        """Retrieves the user account and quota information."""
[66]197        info = self.getUser(username)
[60]198        if info :
199            quotas = self.getUserPQuotas(username)
[66]200            return User(username, info, \
201                                  self.getUserPQuotas(username), \
202                                  self.getPrinters())
[47]203   
[63]204class PyKotIconGrid(gridlib.Grid) :   
205    """A class for user print quota entries."""
[66]206    def __init__(self, parent, user) :
[63]207        gridlib.Grid.__init__(self, parent, -1)
[75]208        nbrows = len(user.Quotas.keys())
209        nbcols = 4
[67]210        if user.LimitBy == "balance" :
[75]211            nbrows += 1
212            nbcols -= 1
213        self.CreateGrid(nbrows, nbcols)
[63]214        self.EnableEditing(False)
[67]215        if user.LimitBy == "balance" :
216            self.SetColLabelValue(0, _("Page Counter"))
217            self.SetColLabelValue(1, _("Price per Page"))
218            self.SetColLabelValue(2, _("Price per Job"))
[63]219            attr = gridlib.GridCellAttr()
[67]220            attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
221            attr.SetReadOnly(True)
[63]222            colour = wx.GREEN
[67]223            if user.Balance <= 0.0 :
224                colour = wx.RED
[63]225            attr.SetBackgroundColour(colour)       
[67]226            self.SetColAttr(0, attr)
227            attr = gridlib.GridCellAttr()
228            attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
229            attr.SetReadOnly(True)
230            self.SetColAttr(1, attr)
231            self.SetColAttr(2, attr)
232            i = 0
233            for printername in user.Printers.keys() :
234                printer = user.Printers[printername]
235                quota = user.Quotas[printername]
236                self.SetRowLabelValue(i, printername)
237                self.SetCellValue(i, 0, str(quota.PageCounter))
238                self.SetCellValue(i, 1, str(printer.PricePerPage))
239                self.SetCellValue(i, 2, str(printer.PricePerJob))
240                i += 1
[75]241            self.SetRowLabelValue(i, "")
242            self.SetCellValue(i, 0, _("CREDITS :") + (" %.2f" % user.Balance))
243            self.SetCellAlignment(i, 0, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
[67]244        else :   
245            self.SetColLabelValue(0, _("Page Counter"))
246            self.SetColLabelValue(1, _("Soft Limit"))
247            self.SetColLabelValue(2, _("Hard Limit"))
248            self.SetColLabelValue(3, _("Date Limit"))
249            attr = gridlib.GridCellAttr()
250            attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
251            attr.SetReadOnly(True)
252            self.SetColAttr(0, attr)
253            attr = gridlib.GridCellAttr()
254            attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
255            attr.SetReadOnly(True)
256            self.SetColAttr(1, attr)
257            self.SetColAttr(2, attr)
258            self.SetColAttr(3, attr)
259            i = 0
260            for printername in user.Quotas.keys() :
261                quota = user.Quotas[printername]
262                self.SetRowLabelValue(i, printername)
263                self.SetCellValue(i, 0, str(quota.PageCounter))
264                self.SetCellValue(i, 1, str(quota.SoftLimit))
265                self.SetCellValue(i, 2, str(quota.HardLimit))
266                self.SetCellValue(i, 3, str(quota.DateLimit))
267                colour = wx.GREEN
268                if quota.SoftLimit is not None :
269                    if quota.PageCounter >= quota.SoftLimit :
270                        colour = wx.RED
271                elif quota.HardLimit is not None :       
272                    if quota.PageCounter >= quota.HardLimit :
273                        colour = wx.RED
274                self.SetCellBackgroundColour(i, 0, colour)       
275                i += 1
[65]276        self.AutoSize()
[80]277        self.Refresh()
[63]278           
[58]279class PyKotIcon(wxPython.wx.wxFrame):
[63]280    """Main class."""
281    def __init__(self, parent, id):
282        wxPython.wx.wxFrame.__init__(self, parent, -1, \
[65]283               _("PyKota Print Quota for user %s") % getCurrentUserName(), \
284               size = (460, -1), \
[66]285               style = wxPython.wx.wxDEFAULT_FRAME_STYLE \
[72]286                     | wxPython.wx.wxSIZE_AUTO_HEIGHT \
287                     | wxPython.wx.wxSIZE_AUTO_WIDTH \
288                     | wxPython.wx.wxICONIZE \
[66]289                     | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
[79]290        try :             
291            self.tbicon = wxPython.wx.wxTaskBarIcon()
292        except AttributeError :   
293            self.tbicon = None # No taskbar icon facility, old wxWidgets maybe
[72]294       
[76]295        self.greenicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-green.ico"), \
[58]296                                  wxPython.wx.wxBITMAP_TYPE_ICO)
[76]297        self.redicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-red.ico"), \
[63]298                                  wxPython.wx.wxBITMAP_TYPE_ICO)
[72]299       
[63]300        self.SetIcon(self.greenicon)
[79]301        if self.tbicon is not None :
302            self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
303            wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
304            wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
[63]305       
[79]306            self.TBMENU_RESTORE = wx.NewId()
307            self.TBMENU_CLOSE = wx.NewId()
308            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
309                                              self.OnTaskBarActivate)
310            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
311                                              self.OnTaskBarClose)
312            self.menu = wxPython.wx.wxMenu()
313            self.menu.Append(self.TBMENU_RESTORE, _("Show Print Quota"))
314            self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
315       
[58]316        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
[64]317        wxPython.wx.EVT_CLOSE(self, self.OnClose)
318       
[59]319        self.TBTIMER = wx.NewId()
[63]320        self.chrono = wxPython.wx.wxTimer(self, self.TBTIMER)
321        wxPython.wx.EVT_TIMER(self, self.TBTIMER, self.OnChronoTimer)
[59]322       
323        self.User = None
324        self.networkInterface = CGINetworkInterface(DUMPYKOTA_URL)
[63]325        self.inTimer = False
[65]326        self.chrono.Start(250) # first time in 0.25 second
[58]327
[59]328    def OnChronoTimer(self, event) :
329        """Retrieves user's data quota information."""
[63]330        # we stop it there, needed because we want to
331        # change the delay the first time.
332        self.chrono.Stop()   
333        if self.inTimer is False : # avoids re-entrance
334            self.inTimer = True
[66]335            self.User = self.networkInterface.getUserInfo(getCurrentUserName())
[63]336            if self.User.LimitBy == "balance" :
337                if self.User.Balance <= 0.0 :
[72]338                    self.SetIcon(self.redicon)
[79]339                    if self.tbicon is not None :
340                        self.tbicon.SetIcon(self.redicon, "PyKotIcon")
[63]341                else :   
[72]342                    self.SetIcon(self.greenicon)
[79]343                    if self.tbicon is not None :
344                        self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
[63]345            else :       
346                isRed = False
[66]347                for q in self.User.Quotas.keys() :
348                    quota = self.User.Quotas[q]
349                    if quota.SoftLimit is not None :
350                        if quota.PageCounter >= quota.SoftLimit :
[63]351                            isRed = True
352                            break
[66]353                    elif quota.HardLimit is not None :       
354                        if quota.PageCounter >= quota.HardLimit :
[63]355                            isRed = True
356                            break
357                if isRed is True :
[72]358                    self.SetIcon(self.redicon)
[79]359                    if self.tbicon is not None :
360                        self.tbicon.SetIcon(self.redicon, "PyKotIcon")
[63]361                else :   
[72]362                    self.SetIcon(self.greenicon)
[79]363                    if self.tbicon is not None :
364                        self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
[67]365            if hasattr(self, "quotasgrid") :   
366                self.quotasgrid.Close()
367                self.quotasgrid.Destroy()
368                del self.quotasgrid
369            self.quotasgrid = PyKotIconGrid(self, self.User)   
[63]370            self.inTimer = False
371        # Now we want it every 3 minutes   
[64]372        #self.chrono.Start(1000 * 60 * 3) # every 3 minutes
373        self.chrono.Start(1000 * 20) # every 20 seconds
[59]374   
375    def OnIconify(self, event) :
[72]376        self.Hide()
[58]377
[59]378    def OnTaskBarActivate(self, event) :
379        if self.IsIconized() :
[58]380            self.Iconize(False)
[59]381        if not self.IsShown() :
[58]382            self.Show(True)
383        self.Raise()
384
[64]385    def OnClose(self, event) :
[59]386        if hasattr(self, "chrono") :
387            self.chrono.Stop()
388            del self.chrono
[66]389        if hasattr(self, "quotasgrid") :
390            self.quotasgrid.Close()
391            self.quotasgrid.Destroy()
392            del self.quotasgrid
[58]393        if hasattr(self, "menu") :
[64]394            self.menu.Destroy()
[58]395            del self.menu
[59]396        if hasattr(self, "tbicon") :
[58]397            self.tbicon.Destroy()
398            del self.tbicon
399        self.Destroy()
400
[59]401    def OnTaskBarMenu(self, evt) :
[58]402        self.tbicon.PopupMenu(self.menu)
403
[59]404    def OnTaskBarClose(self, evt) :
[58]405        self.Close()
406
[72]407class PyKotIconApp(wx.PySimpleApp):
[58]408    def OnInit(self) :
[63]409        try :
410            frame = PyKotIcon(None, -1)
411        except :   
412            crashed()
413        else :   
414            frame.Show(True)
[58]415        return True
416       
[63]417def main():
418    """Program's entry point."""
[65]419    try :
420        locale.setlocale(locale.LC_ALL, "")
421    except (locale.Error, IOError) :
422        sys.stderr.write("Problem while setting locale.\n")
423    try :
424        gettext.install("pykoticon")
425    except :
426        gettext.NullTranslations().install()
[63]427    app = PyKotIconApp()
428    app.MainLoop()
429   
[58]430def crashed() :   
431    """Minimal crash method."""
432    import traceback
433    lines = []
434    for line in traceback.format_exception(*sys.exc_info()) :
435        lines.extend([l for l in line.split("\n") if l])
436    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
437    sys.stderr.write(msg)
438    sys.stderr.flush()
[47]439   
[58]440def test() :
441    """Runs in test mode (console)."""
[60]442    if len(sys.argv) >= 3 :
443        username = sys.argv[2] 
444    else :   
[65]445        username = getCurrentUserName()
[58]446    net = CGINetworkInterface(DUMPYKOTA_URL)
[66]447    user = net.getUserInfo(username)
[65]448    print "UserName : ", user.UserName
[58]449    print "LimitBy : ", user.LimitBy
450    print "Balance : ", user.Balance
[66]451    for printername in user.Quotas.keys() :
452        quota = user.Quotas[printername]
453        print "\tPrinterName : ", printername
454        print "\tPageCounter : ", quota.PageCounter
455        print "\tSoftLimit : ", quota.SoftLimit
456        print "\tHardLimit : ", quota.HardLimit
457        print "\tDateLimit : ", quota.DateLimit
[58]458        print
[66]459    for printername in user.Printers.keys() :
460        printer = user.Printers[printername]
461        print "\tPrinterName : ", printername
462        print "\tPrice per Page : ", printer.PricePerPage
463        print "\tPrice per Job : ", printer.PricePerJob
464        print
[47]465
[58]466if __name__ == '__main__':
[60]467    if (len(sys.argv) >= 2) and (sys.argv[1] == "--test") :
[58]468        test()
469    else :   
470        main()
471   
Note: See TracBrowser for help on using the browser.