""" Copyright 2004 Jim Bublitz Terms and Conditions Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the copyright holder. """ from dcop import DCOPClient from qt import QString, QCString, QByteArray, QDataStream, IO_ReadOnly, IO_WriteOnly from kdecore import dcop_add, dcop_next from kdecore import KURL numericTypes = ["char", "short", "int", "long", "uchar", "ushort", "uint", "ulong", "unsigned char", "unsigned short", "unsigned int", "unsigned long", "Q_INT32", "pid_t", "float", "double"] stringTypes = ["QString", "QCString"] """ (Most of this code is adapted from pydcop in kde-bindings, written by Torben Weis and Julian Rockey) The three classes below (DCOPApp, DCOPObj and DCOPMeth) allow transparent Python calls to DCOP methods. For example: d = DCOPApp ("kicker", dcop) (where "kicker" is the complete name of an application and 'dcop' is the dcopClient instance owned by the KApplication creating the DCOPApp instance) creates a DCOPApp instance. All of the classes in this file "borrow" a DCOPClient instance from the calling application. d.objects will return a list of the DCOP objects the application supplies. o = d.object ("Panel") will return a DCOPObj corresponding to applications "Panel" DCOP object. Similarly: o.methods will return a list of the methods the object supplies and m = o.method ("panelSize") will return a DCOPMeth corresponding to Panel's panelSize() method. The m instance also holds the methods return type, list of argument types (argtypes) and argument names (argnames). m.valid is a boolean which indicates if the method encapsulated by m is a valid method for the application/object specified. However it isn't necessary to explicitly create the DCOPObj and DCOPMeth. d.Panel.panelSize.valid for example, will also indicate if the method is valid without creating the intermediate 'o' and 'm' instances explicitly. d = DCOPApp ("kicker", dcop) ok, res = d.Panel.panelSize () is all the code necessary to perform the indicated DCOP call and return the value the call returns. In this case, panelSize takes no arguments and returns an int. 'ok' returns the status of the DCOP call (success = True, failure = False). ok = d.Panel.addURLButton (QString ("http://www.kde.org")) would call addURLButton with the required argument, and return nothing but the DCOP call status(since its return type is 'void'). Note that to instantiate a DCOPObj directly, you need to have a valid DCOPApp to pass to DCOPObj's __init__ method. Similarly, DCOPMeth requires a valid DCOPOBject. For example: d = DCOPApp ("kicker", dcop) o = DCOPObj (d, "Panel") m = DCOPMeth (o, "panelSize") or m = DCOPMeth (DCOPObj (DCOPApp ("kicker", dcop), "Panel"), "panelSize") """ class DCOPApp(object): """ An object corresponding to an application with a DCOP interface Can return a list of the DCOP objects the application exposes, or create and return an instance of a specific DCOP object. """ def __init__ (self, name, client): self.appname = name self.appclient = client def __getattr__ (self, item ): if item == "__repr__": return object.__repr__ if item == "__str__": return object.__str__ if item == "__call__": return object.__call__ if item == "objects": objs, ok = self.appclient.remoteObjects (self.appname) if ok: return objs else: return None return DCOPObj (self, item) def object (self, object): return DCOPObj (self, object) class DCOPObj(object): """ An object corresponding to a specific DCOP object owned by a specific application with a DCOP interface Can return a list of the DCOP methods the object exposes, or create and return an instance of a specific DCOP method. """ def __init__ (self, *args): if isinstance (args [0], str) or isinstance (args [0], QCString) or isinstance (args [0], QString): self.appname = args [0] self.objclient = args [1] self.objname = args [2] else: self.appname = args [0].appname self.objname = args [1] self.objclient = args [0].appclient self.objmethods = self.getMethods () def __repr__( self ): return "DCOPObj(%s,%s)" % (self.appname, self.objname) def __str__( self ): return "DCOPObj(%s,%s)" % (self.appname, self.objname) def __getattr__( self, item ): if item == "__repr__": return object.__repr__ if item == "__str__": return object.__str__ if item == "__call__": return object.__call__ if item == "methods": return self.objmethods return DCOPMeth (self, item) def getMethods (self): flist, ok = self.objclient.remoteFunctions (self.appname, self.objname) if ok: return flist else: return None def getMethodNames (self): if not self.objmethods: return None methNames = [] for meth in self.objmethods: head, tail = str(meth).split (" ", 1) if not tail: return None i = tail.find ("(") if i < 1: return None methNames.append (tail [:i].strip ()) return methNames def method(self, method): return DCOPMeth (self, method) class DCOPMeth(object): """ An object corresponding to a specific DCOP method owned by a specific DCOP object. """ def __init__ (self, dcopObj, name): self.appname = dcopObj.appname self.objname = dcopObj.objname self.methname = name self.client = dcopObj.objclient self.methods = dcopObj.objmethods self.valid = self.parseMethod () if not self.valid: self.fcnname = self.method = self.rtype = self.argtypes = self.argnames = None def __repr__( self ): return "DCOPMeth(%s,%s,%s)" % (self.appname, self.objname, self.methname) def __str__( self ): return "DCOPMeth(%s,%s,%s)" % (self.appname, self.objname, self.methname) def __call__ (self, *args): return self.dcop_call (args) def dcop_call (self, args): # method valid? if not self.valid: return False, None #Remove escape characters # if self.objname [0] == '_': # self.objname = self.objname [1:] # if self.name [0] == '_': # self.name = self.name [1:] # correct number of args? if self.argtypes and len (args) != len (self.argtypes): return False, None ok, replyType, replyData = self.client.call (self.appname, self.objname, self.fcnname, self.marshall (args)) if ok: return ok, self.unmarshall (replyData, replyType) else: return ok, None def findMethod (self): for meth in self.methods: head, tail = str(meth).split (" ", 1) if not tail: return None i = tail.find ("(") if i < 1: return None if self.methname == tail [:i].strip (): self.method = meth return True return False def parseMethod (self): if not self.methods or not self.findMethod (): return False self.method = str (DCOPClient.normalizeFunctionSignature (self.method)).replace (">", "> ") self.rtype, tail = self.method.split (" ", 1) if not tail: return False self.rtype = self.rtype.strip () i = tail.find ("(") if i < 1: return False self.fcnname = tail [:i].strip () + "(" self.argtypes = [] self.argnames = [] args = tail [i + 1 : -1].split (",") if args and args != [""]: for arg in args: argInstance = arg.split () self.argtypes.append (argInstance [0].strip ()) if len (argInstance) == 2: self.argnames.append (argInstance [1].strip ()) else: self.argnames.append ("") self.fcnname += ",".join (self.argtypes) + ")" return True def marshall (self, args): if not self.valid: return None data = QByteArray () if self.argtypes == []: return data params = QDataStream (data, IO_WriteOnly) for i in range (len (args)): # handle numeric type (char, short, int, long, unsigned types, float, double) if self.argtypes [i] in numericTypes: dcop_add (params, args [i], self.argtypes [i]) # handle string type when QString or QCString expected elif self.argtypes [i] in stringTypes and isinstance (args [i], str): dcop_add (params, eval ("%s('%s')" % (self.argtypes [i], args [i]))) # handle QMap and QValueList which PyKDE represents as a Python dict or list elif self.argtypes [i].startswith ("QMap") or self.argtypes [i].startswith ("QValueList"): dcop_add (params, args [i], self.argtypes [i]) # all other types elif eval ("isinstance (args [i], %s)" % self.argtypes [i]): dcop_add (params, args [i]) else: raise return data def unmarshall (self, data, type_): s = QDataStream (data, IO_ReadOnly) return dcop_next (s, type_)