root / pykoticon / trunk / bin / pykoticon @ 103

Revision 103, 12.1 kB (checked in by jerome, 18 years ago)

Generic dialog sort of works. Some cleaning and result data extraction
still need to be done.

  • 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_askDatas(self, labels, varnames, varvalues) :
93        """Asks some textual datas defined by a list of labels, a list of variables' names and a list of variables values in a mapping."""
94        wx.CallAfter(self.frame.askDatas, labels, varnames, varvalues)
95        # ugly, isn't it ?
96        #while self.frame.dialogAnswer is None :
97        #    time.sleep(0.1)
98        # TODO : add value extraction and return a mapping
99        self.frame.dialogAnswer = None # prepare for next call, just in case
100        return True
101       
102    def export_showDialog(self, message, yesno) :
103        """Opens a notification or confirmation dialog."""
104        wx.CallAfter(self.frame.showDialog, message, yesno)
105        # ugly, isn't it ?
106        while self.frame.dialogAnswer is None :
107            time.sleep(0.1)
108        retcode = self.frame.dialogAnswer   
109        self.frame.dialogAnswer = None # prepare for next call, just in case
110        return retcode
111       
112    def export_nop(self) :   
113        """Does nothing, but allows a clean shutdown from the frame itself."""
114        self.logDebug("No operation !")
115        return True
116       
117    def verify_request(self, request, client_address) :
118        """Ensures that requests which don't come from the print server are rejected."""
119        (client, port) = client_address
120        if socket.gethostbyname(self.printServer) == client :
121            self.logDebug("%s accepted." % client)
122            return True
123        else :
124            # Unauthorized access !
125            self.logDebug("%s rejected." % client)
126            return False
127       
128    def _dispatch(self, method, params) :   
129        """Ensure that only export_* methods are available."""
130        return getattr(self, "export_%s" % method)(*params)
131       
132    def mainloop(self) :
133        """XML-RPC Server's main loop."""
134        self.register_function(self.export_askDatas)
135        self.register_function(self.export_showDialog)
136        self.register_function(self.export_quitApplication)
137        self.register_function(self.export_nop)
138        while not self.frame.quitEvent.isSet() :
139            self.handle_request()
140        self.server_close()   
141        sys.exit(0)
142   
143class PyKotIcon(wx.Frame):
144    """Main class."""
145    def __init__(self, parent, id):
146        self.dialogAnswer = None
147        wx.Frame.__init__(self, parent, wx.ID_ANY, \
148               _("PyKota info for %s") % getCurrentUserName(), \
149               size = (-1, -1), \
150               style = wxPython.wx.wxDEFAULT_FRAME_STYLE \
151                     | wxPython.wx.wxSIZE_AUTO_HEIGHT \
152                     | wxPython.wx.wxSIZE_AUTO_WIDTH \
153                     | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
154#        try :             
155#            self.tbicon = wxPython.wx.wxTaskBarIcon()
156#        except AttributeError :   
157#            self.tbicon = None # No taskbar icon facility, old wxWidgets maybe
158#       
159#        self.greenicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-green.ico"), \
160#                                  wxPython.wx.wxBITMAP_TYPE_ICO)
161#        self.redicon = wxPython.wx.wxIcon(os.path.join(iconsdir, "pykoticon-red.ico"), \
162#                                  wxPython.wx.wxBITMAP_TYPE_ICO)
163#       
164#        self.SetIcon(self.greenicon)
165#        if self.tbicon is not None :
166#            self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
167#            wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
168#            wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
169#       
170#            self.TBMENU_RESTORE = wx.NewId()
171#            self.TBMENU_CLOSE = wx.NewId()
172#            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
173#                                              self.OnTaskBarActivate)
174#            wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
175#                                              self.OnTaskBarClose)
176#            self.menu = wxPython.wx.wxMenu()
177#            self.menu.Append(self.TBMENU_RESTORE, _("Show Print Quota"))
178#            self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
179#       
180        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
181        wxPython.wx.EVT_CLOSE(self, self.OnClose)
182        self.Show(True)
183       
184    def closeServer(self) :   
185        """Tells the xml-rpc server to exit."""
186        if not self.quitEvent.isSet() :
187            self.quitEvent.set()
188        server = xmlrpclib.ServerProxy("http://localhost:%s" % self.port)   
189        try :
190            # wake the server with an empty request
191            # for it to see the event object
192            # which has just been set
193            server.nop()
194        except :   
195            # Probably already stopped
196            pass
197       
198    def postInit(self, printserver, localport) :   
199        """Starts the XML-RPC server."""
200        self.quitEvent = threading.Event()
201        self.port = localport
202        self.server = MyXMLRPCServer(self, printserver, localport, debug=True)
203   
204    def OnIconify(self, event) :
205        self.Hide()
206
207    def OnTaskBarActivate(self, event) :
208        #if self.IsIconized() :
209        #    self.Iconize(False)
210        if not self.IsShown() :
211            self.Show(True)
212        self.Raise()
213
214    def OnClose(self, event) :
215        sys.stderr.write("Close event !\n")
216        self.closeServer()
217        if hasattr(self, "menu") :
218            self.menu.Destroy()
219            del self.menu
220        if hasattr(self, "tbicon") and self.tbicon :
221            self.tbicon.Destroy()
222            del self.tbicon
223        self.Destroy()
224
225    def OnTaskBarMenu(self, event) :
226        #if self.tbicon :
227        #    self.tbicon.PopupMenu(self.menu)
228        pass
229
230    def OnTaskBarClose(self, event) :
231        self.Close()
232       
233    def showDialog(self, message, yesno) :
234        """Opens a notification dialog."""
235        self.dialogAnswer = None
236        if yesno :
237            caption = _("Confirmation")
238            style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION
239        else :
240            caption = _("Information")
241            style = wx.OK | wx.ICON_INFORMATION
242        style |= wx.STAY_ON_TOP   
243        dialog = wx.MessageDialog(self, message, caption, style)
244        self.dialogAnswer = ((dialog.ShowModal() == wx.ID_NO) and "CANCEL") or "OK"
245        dialog.Destroy()
246       
247    def askDatas(self, labels, varnames, varvalues) :
248        """Opens a dialog box asking for data entry."""
249        # use it this way : self.askDatas(["Username", "Password", "Billing code"], ["username", "password", "billingcode"])
250        frame = wx.Frame(self, wx.ID_ANY, \
251               _("Enter PyKota information"), \
252               size = (-1, -1), \
253               style = wxPython.wx.wxDEFAULT_FRAME_STYLE \
254                     | wxPython.wx.wxSIZE_AUTO_HEIGHT \
255                     | wxPython.wx.wxSIZE_AUTO_WIDTH \
256                     | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
257        self.dialogAnswer = None
258        values = {}
259        vsizer = wx.BoxSizer(wx.VERTICAL)
260        for i in range(len(varnames)) :
261            varname = varnames[i]
262            values[varname] = None
263            try :
264                label = labels[i]
265            except IndexError :   
266                label = ""
267            labelid = wx.NewId()   
268            varid = wx.NewId()
269            label = wx.StaticText(frame, labelid, label)
270            variable = wx.TextCtrl(frame, varid, varvalues.get(varname, ""))
271            hsizer = wx.BoxSizer(wx.HORIZONTAL)
272            hsizer.Add(label, 0, wx.ALIGN_CENTER | wx.ALIGN_RIGHT | wx.ALL, 5)
273            hsizer.Add(variable, 0, wx.ALIGN_CENTER | wx.ALIGN_LEFT | wx.ALL, 5)
274            vsizer.Add(hsizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
275        okbutton = wx.Button(frame, wx.ID_ANY, "OK")   
276        vsizer.Add(okbutton, 0, wx.ALIGN_CENTER | wx.ALL, 5)
277        frame.SetAutoLayout(True)
278        frame.SetSizerAndFit(vsizer)
279        frame.Layout()
280        frame.Show(True)
281
282class PyKotIconApp(wx.PySimpleApp):
283    def OnInit(self) :
284        self.frame = PyKotIcon(None, -1)
285        return True
286       
287    def postInit(self, printserver, localport) :   
288        """Continues processing."""
289        self.frame.postInit(printserver, localport)
290        self.frame.Show(True)
291       
292def main(printserver, localport):
293    """Program's entry point."""
294    try :
295        locale.setlocale(locale.LC_ALL, "")
296    except (locale.Error, IOError) :
297        sys.stderr.write("Problem while setting locale.\n")
298    try :
299        gettext.install("pykoticon")
300    except :
301        gettext.NullTranslations().install()
302    app = PyKotIconApp()
303    try :
304        localport = int(localport)   
305    except (TypeError, ValueError) :   
306        raise ValueError, "Invalid TCP port parameter %s\n" % localport
307    app.postInit(printserver, localport)
308    app.MainLoop()
309   
310def crashed() :   
311    """Minimal crash method."""
312    import traceback
313    lines = []
314    for line in traceback.format_exception(*sys.exc_info()) :
315        lines.extend([l for l in line.split("\n") if l])
316    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
317    sys.stderr.write(msg)
318    sys.stderr.flush()
319   
320if __name__ == '__main__':
321    if len(sys.argv) >= 2 :
322        arg = sys.argv[1]
323        if arg in ("-v", "--version") :   
324            print "0.3"
325        elif arg in ("-h", "--help") :   
326            sys.stderr.write("usage : pykoticon  pykota_server_hostname_or_ip_address  localTCPPort\n")
327        else :
328            main(*sys.argv[1:3])
329    else :   
330        sys.stderr.write("usage : pykoticon  pykota_server_hostname_or_ip_address  localTCPPort\n")
Note: See TracBrowser for help on using the browser.