root / pykoticon / trunk / bin / pykoticon @ 91

Revision 91, 10.2 kB (checked in by jerome, 18 years ago)

Confirmation dialog seems to work fine.

  • Property svn:keywords set to Id
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKotIcon - Client side helper for PyKota
5#
6# (c) 2003, 2004, 2005, 2006 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_openConfirmDialog(self, printername, username, jobid, jobtitle, jobsize) :   
92        """Opens a dialog to ask the user to confirm or cancel the print job.
93       
94           Returns True to confirm, False to cancel.
95        """   
96        wx.CallAfter(self.frame.askConfirmation, printername, username, jobid, jobtitle, jobsize)
97        while self.frame.askConfirmationResult is None :
98            time.sleep(0.5)
99        retcode = self.frame.askConfirmationResult   
100        self.frame.askConfirmationResult = None
101        return retcode
102       
103    def export_nop(self) :   
104        """Does nothing, but allows a clean shutdown from the frame itself."""
105        self.logDebug("No operation !")
106        return True
107       
108    def verify_request(self, request, client_address) :
109        """Ensures that requests which don't come from the print server are rejected."""
110        (client, port) = client_address
111        if socket.gethostbyname(self.printServer) == client :
112            self.logDebug("%s accepted." % client)
113            return True
114        else :
115            # Unauthorized access !
116            self.logDebug("%s rejected." % client)
117            return False
118       
119    def _dispatch(self, method, params) :   
120        """Ensure that only export_* methods are available."""
121        return getattr(self, "export_%s" % method)(*params)
122       
123    def mainloop(self) :
124        """XML-RPC Server's main loop."""
125        self.register_function(self.export_openConfirmDialog)
126        self.register_function(self.export_quitApplication)
127        self.register_function(self.export_nop)
128        while not self.frame.quitEvent.isSet() :
129            self.logDebug("Loop!")
130            self.handle_request()
131        self.server_close()   
132        try :   
133            if not self.frame.closing :
134                self.frame.Close() 
135        except :   
136            # Probably already closed
137            pass
138        sys.exit(0)
139   
140class PyKotIcon(wx.Frame):
141    """Main class."""
142    def __init__(self, parent, id):
143        self.askConfirmationResult = None
144        self.closing = False
145        wx.Frame.__init__(self, parent, wx.ID_ANY, \
146               _("PyKota for user %s") % getCurrentUserName(), \
147               size = (-1, -1), \
148               style = wxPython.wx.wxDEFAULT_FRAME_STYLE \
149                     | wxPython.wx.wxSIZE_AUTO_HEIGHT \
150                     | wxPython.wx.wxSIZE_AUTO_WIDTH \
151                     | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
152#        try :             
153#            self.tbicon = wxPython.wx.wxTaskBarIcon()
154#        except AttributeError :   
155#            self.tbicon = None # No taskbar icon facility, old wxWidgets maybe
156#       
157#        self.greenicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-green.ico"), \
158#                                  wxPython.wx.wxBITMAP_TYPE_ICO)
159#        self.redicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-red.ico"), \
160#                                  wxPython.wx.wxBITMAP_TYPE_ICO)
161#       
162#        self.SetIcon(self.greenicon)
163#        if self.tbicon is not None :
164#            self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
165#            wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
166#            wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
167#       
168#            self.TBMENU_RESTORE = wx.NewId()
169#            self.TBMENU_CLOSE = wx.NewId()
170#            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
171#                                              self.OnTaskBarActivate)
172#            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
173#                                              self.OnTaskBarClose)
174#            self.menu = wxPython.wx.wxMenu()
175#            self.menu.Append(self.TBMENU_RESTORE, _("Show Print Quota"))
176#            self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
177#       
178        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
179        wxPython.wx.EVT_CLOSE(self, self.OnClose)
180        self.Show(True)
181       
182    def closeServer(self) :   
183        """Tells the xml-rpc server to exit."""
184        if not self.quitEvent.isSet() :
185            self.quitEvent.set()
186        server = xmlrpclib.ServerProxy("http://localhost:%s" % self.port)   
187        try :
188            server.nop()
189        except :   
190            # Probably already stopped
191            pass
192       
193    def postInit(self, printserver, localport) :   
194        """Starts the XML-RPC server."""
195        self.quitEvent = threading.Event()
196        self.port = localport
197        self.server = MyXMLRPCServer(self, printserver, localport, debug=True)
198   
199    def OnIconify(self, event) :
200        self.Hide()
201
202    def OnTaskBarActivate(self, event) :
203        #if self.IsIconized() :
204        #    self.Iconize(False)
205        if not self.IsShown() :
206            self.Show(True)
207        self.Raise()
208
209    def OnClose(self, event) :
210        sys.stderr.write("Close event !\n")
211        self.closing = True
212        self.closeServer()
213        if hasattr(self, "menu") :
214            self.menu.Destroy()
215            del self.menu
216        if hasattr(self, "tbicon") and self.tbicon :
217            self.tbicon.Destroy()
218            del self.tbicon
219        self.Destroy()
220
221    def OnTaskBarMenu(self, event) :
222        #if self.tbicon :
223        #    self.tbicon.PopupMenu(self.menu)
224        pass
225
226    def OnTaskBarClose(self, event) :
227        self.Close()
228       
229    def askConfirmation(self, printername, username, jobid, jobtitle, jobsize) :
230        """Asks for confirmation before printing."""
231        message = _("""Hello %(username)s,
232       
233                     You sent job %(jobid)s (%(jobtitle)s) to printer %(printername)s.
234                                   
235                     This job seems to be %(jobsize)s pages long.
236                                   
237                     Please confirm or cancel.""") % locals()
238                     
239        dialog = wx.MessageDialog(self, message, _("Confirmation"), wx.OK | wx.CANCEL)
240        self.askConfirmationResult = dialog.ShowModal()
241        dialog.Destroy()
242
243class PyKotIconApp(wx.PySimpleApp):
244    def OnInit(self) :
245        self.frame = PyKotIcon(None, -1)
246        return True
247       
248    def postInit(self, printserver, localport) :   
249        """Continues processing."""
250        self.frame.postInit(printserver, localport)
251        self.frame.Show(True)
252       
253def main(printserver, localport):
254    """Program's entry point."""
255    try :
256        locale.setlocale(locale.LC_ALL, "")
257    except (locale.Error, IOError) :
258        sys.stderr.write("Problem while setting locale.\n")
259    try :
260        gettext.install("pykoticon")
261    except :
262        gettext.NullTranslations().install()
263    app = PyKotIconApp()
264    try :
265        localport = int(localport)   
266    except (TypeError, ValueError) :   
267        raise ValueError, "Invalid TCP port parameter %s\n" % localport
268    app.postInit(printserver, localport)
269    app.MainLoop()
270   
271def crashed() :   
272    """Minimal crash method."""
273    import traceback
274    lines = []
275    for line in traceback.format_exception(*sys.exc_info()) :
276        lines.extend([l for l in line.split("\n") if l])
277    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
278    sys.stderr.write(msg)
279    sys.stderr.flush()
280   
281if __name__ == '__main__':
282    if len(sys.argv) >= 2 :
283        arg = sys.argv[1]
284        if arg in ("-v", "--version") :   
285            print "0.3"
286        elif arg in ("-h", "--help") :   
287            print "usage : pykoticon printserver_hostname localport"
288        else :
289            main(*sys.argv[1:3])
290    else :   
291        main("localhost", "7654")
Note: See TracBrowser for help on using the browser.