root / pykoticon / trunk / bin / pykoticon @ 96

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

Improved the confirmation dialog box.

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