root / pykoticon / trunk / bin / pykoticon @ 88

Revision 88, 9.1 kB (checked in by jerome, 18 years ago)

Now seems to close correctly both sides.

  • Property svn:keywords set to Id
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKotIcon - Windows System Tray Icon for PyKota
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#
21# $Id$
22#
23#
24
25import sys
26import os
27import urllib
28import urllib2
29import locale
30import gettext
31import socket
32import threading
33import xmlrpclib
34import SimpleXMLRPCServer
35
36import time
37
38if sys.platform == "win32" :
39    isWindows = 1
40    try :
41        import win32api
42    except ImportError :   
43        raise ImportError, "Mark Hammond's Win32 Extensions are missing. Please install them."
44    else :   
45        iconsdir = os.path.split(sys.argv[0])[0]
46else :       
47    isWindows = 0
48    iconsdir = "/usr/share/pykoticon"   # TODO : change this
49    import pwd
50   
51try :   
52    import wxPython.wx
53    import wx
54    hasWxPython = 1
55except ImportError :   
56    hasWxPython = 0
57    raise ImportError, "wxPython is missing. Please install it."
58   
59def getCurrentUserName() :
60    """Retrieves the current user's name."""
61    if isWindows :
62        return win32api.GetUserName()
63    else :   
64        try :
65            return pwd.getpwuid(os.geteuid())[0]
66        except :
67            return "** Unknown **"
68       
69class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer) :
70    """My own server class."""
71    allow_reuse_address = True
72    def __init__(self, frame, printserver, localport, debug=False) :
73        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, ('0.0.0.0', localport))
74        self.printServer = printserver
75        self.frame = frame
76        self.debug = debug
77        loop = threading.Thread(target=self.mainloop)
78        loop.start()
79       
80    def logDebug(self, message) :   
81        """Logs a debug message if debug mode is active."""
82        if self.debug :
83            sys.stderr.write("%s\n" % message)
84           
85    def export_quitApplication(self) :   
86        """Makes the application quit."""
87        self.logDebug("Remote host asked to close the application.")
88        self.frame.quitEvent.set()
89        return True
90       
91    def export_openDialog(self) :   
92        """Opens a dialog to ask username, password, etc..."""
93        self.logDebug("Open dialog !")
94        return ("jerome", "blah")
95       
96    def export_nop(self) :   
97        """Does nothing, but allows a clean shutdown from the frame itself."""
98        self.logDebug("No operation !")
99        return True
100       
101    def verify_request(self, request, client_address) :
102        """Ensures that requests which don't come from the print server are rejected."""
103        (client, port) = client_address
104        if socket.gethostbyname(self.printServer) == client :
105            self.logDebug("%s accepted." % client)
106            return True
107        else :
108            # Unauthorized access !
109            self.logDebug("%s rejected." % client)
110            return False
111       
112    def _dispatch(self, method, params) :   
113        """Ensure that only export_* methods are available."""
114        return getattr(self, "export_%s" % method)(*params)
115       
116    def mainloop(self) :
117        """XML-RPC Server's main loop."""
118        self.register_function(self.export_openDialog)
119        self.register_function(self.export_quitApplication)
120        self.register_function(self.export_nop)
121        while not self.frame.quitEvent.isSet() :
122            self.logDebug("Loop!")
123            self.handle_request()
124        self.server_close()   
125        try :   
126            if not self.frame.closing :
127                self.frame.Close() 
128        except :   
129            # Probably already closed
130            pass
131        sys.exit(0)
132   
133class PyKotIcon(wx.Frame):
134    """Main class."""
135    def __init__(self, parent, id):
136        wx.Frame.__init__(self, parent, -1, \
137               _("PyKota for user %s") % getCurrentUserName(), \
138               size = (-1, -1), \
139               style = wxPython.wx.wxDEFAULT_FRAME_STYLE \
140                     | wxPython.wx.wxSIZE_AUTO_HEIGHT \
141                     | wxPython.wx.wxSIZE_AUTO_WIDTH \
142                     | wxPython.wx.wxICONIZE \
143                     | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
144        try :             
145            self.tbicon = wxPython.wx.wxTaskBarIcon()
146        except AttributeError :   
147            self.tbicon = None # No taskbar icon facility, old wxWidgets maybe
148       
149        self.greenicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-green.ico"), \
150                                  wxPython.wx.wxBITMAP_TYPE_ICO)
151        self.redicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-red.ico"), \
152                                  wxPython.wx.wxBITMAP_TYPE_ICO)
153       
154        self.SetIcon(self.greenicon)
155        if self.tbicon is not None :
156            self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
157            wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
158            wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
159       
160            self.TBMENU_RESTORE = wx.NewId()
161            self.TBMENU_CLOSE = wx.NewId()
162            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
163                                              self.OnTaskBarActivate)
164            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
165                                              self.OnTaskBarClose)
166            self.menu = wxPython.wx.wxMenu()
167            self.menu.Append(self.TBMENU_RESTORE, _("Show Print Quota"))
168            self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
169       
170        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
171        wxPython.wx.EVT_CLOSE(self, self.OnClose)
172       
173        self.closing = False
174       
175    def closeServer(self) :   
176        """Tells the xml-rpc server to exit."""
177        if not self.quitEvent.isSet() :
178            self.quitEvent.set()
179        server = xmlrpclib.ServerProxy("http://localhost:%s" % self.port)   
180        try :
181            server.nop()
182        except :   
183            # Probably already stopped
184            pass
185       
186    def postInit(self, printserver, localport) :   
187        """Starts the XML-RPC server."""
188        self.quitEvent = threading.Event()
189        self.port = localport
190        self.server = MyXMLRPCServer(self, printserver, localport, debug=True)
191   
192    def OnIconify(self, event) :
193        self.Hide()
194
195    def OnTaskBarActivate(self, event) :
196        if self.IsIconized() :
197            self.Iconize(False)
198        if not self.IsShown() :
199            self.Show(True)
200        self.Raise()
201
202    def OnClose(self, event) :
203        sys.stderr.write("Close event !\n")
204        self.closing = True
205        self.closeServer()
206        if hasattr(self, "menu") :
207            self.menu.Destroy()
208            del self.menu
209        if hasattr(self, "tbicon") and self.tbicon :
210            self.tbicon.Destroy()
211            del self.tbicon
212        self.Destroy()
213
214    def OnTaskBarMenu(self, evt) :
215        if self.tbicon :
216            self.tbicon.PopupMenu(self.menu)
217
218    def OnTaskBarClose(self, evt) :
219        self.Close()
220
221class PyKotIconApp(wx.PySimpleApp):
222    def OnInit(self) :
223        try :
224            self.frame = PyKotIcon(None, -1)
225        except :   
226            crashed()
227        return True
228       
229    def postInit(self, printserver, localport) :   
230        """Continues processing."""
231        self.frame.postInit(printserver, localport)
232        self.frame.Show(True)
233       
234def main(printserver, localport):
235    """Program's entry point."""
236    try :
237        locale.setlocale(locale.LC_ALL, "")
238    except (locale.Error, IOError) :
239        sys.stderr.write("Problem while setting locale.\n")
240    try :
241        gettext.install("pykoticon")
242    except :
243        gettext.NullTranslations().install()
244    app = PyKotIconApp()
245    try :
246        localport = int(localport)   
247    except (TypeError, ValueError) :   
248        raise ValueError, "Invalid TCP port parameter %s\n" % localport
249    app.postInit(printserver, localport)
250    app.MainLoop()
251   
252def crashed() :   
253    """Minimal crash method."""
254    import traceback
255    lines = []
256    for line in traceback.format_exception(*sys.exc_info()) :
257        lines.extend([l for l in line.split("\n") if l])
258    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
259    sys.stderr.write(msg)
260    sys.stderr.flush()
261   
262if __name__ == '__main__':
263    if len(sys.argv) >= 2 :
264        arg = sys.argv[1]
265        if arg in ("-v", "--version") :   
266            print "0.3"
267        elif arg in ("-h", "--help") :   
268            print "usage : pykoticon printserver_hostname localport"
269        else :
270            main(*sys.argv[1:3])
271    else :   
272        main("localhost", "7654")
Note: See TracBrowser for help on using the browser.