You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
7.6 KiB
200 lines
7.6 KiB
"""
|
|
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.
|
|
"""
|
|
|
|
"""
|
|
This is a re-implementation in Python of pcop.cpp written by Torben Weis
|
|
and Julian Rockey, modified for Python and PyKDE.
|
|
|
|
To "DCOP-enable" an application, subclass DCOPExObj (be sure to call the
|
|
base class' __init__ method) and use 'addMethod' to identify the methods
|
|
which will be exposed via DCOP along with names of the Python methods that
|
|
implement the exposed methods.
|
|
|
|
A DCOP client application when doing DCOPCLient.call (...) will end up
|
|
running the 'process' method which demarshalls the arguments, calls the
|
|
specified Python method with the arg values passed in, and marshalls the
|
|
return value to be returned to the caller.
|
|
|
|
DCOPExMeth is basically a data structure to hold the parsed method info
|
|
(name, arg list, return type, signature)
|
|
|
|
This module requires the dcopext module, but only for the numericTypes and
|
|
stringTypes lists
|
|
"""
|
|
|
|
|
|
from dcop import DCOPObject, DCOPClient
|
|
from kdecore import dcop_add, dcop_next
|
|
from qt import QString, QCString, QDataStream, IO_ReadOnly, IO_WriteOnly
|
|
|
|
numericTypes = ["char", "bool", "short", "int", "long", "uchar", "ushort", "uint", "ulong",
|
|
"unsigned char", "unsigned short", "unsigned int", "unsigned long",
|
|
"Q_INT32", "pid_t", "float", "double"]
|
|
stringTypes = ["QString", "QCString"]
|
|
|
|
class DCOPExObj (DCOPObject):
|
|
def __init__ (self, objid = None):
|
|
if isinstance (objid, str):
|
|
DCOPObject.__init__ (self, objid)
|
|
else:
|
|
DCOPObject.__init__ (self)
|
|
|
|
self.methods = {}
|
|
|
|
def process (self, meth, data, replyType, replyData):
|
|
# normalize the method signature received
|
|
meth = str (DCOPClient.normalizeFunctionSignature (meth)).replace (">", "> ")
|
|
|
|
# see if this method is available from us via DCOP
|
|
# if we don't have it, maybe DCOPObject already provides it (eg - qt object)
|
|
if not self.matchMethod (meth):
|
|
return DCOPObject.process(self, meth, data, replyType, replyData);
|
|
|
|
# demarshall the arg list for the actual method call and call the method
|
|
s = QDataStream (data, IO_ReadOnly)
|
|
arglist = []
|
|
count = len (self.method.argtypes)
|
|
if count == 0:
|
|
result = self.method.pymethod ()
|
|
else:
|
|
for i in range (len (self.method.argtypes)):
|
|
arglist.append (dcop_next (s, QCString (self.method.argtypes [i])))
|
|
|
|
result = self.method.pymethod (*arglist)
|
|
|
|
# marshall the result as 'replyData'
|
|
if self.method.rtype != "void":
|
|
s = QDataStream (replyData, IO_WriteOnly)
|
|
if self.method.rtype in numericTypes:
|
|
dcop_add (s, result, self.method.rtype)
|
|
elif self.method.rtype in stringTypes and isinstance (result, str):
|
|
dcop_add (s, eval ("%s('''%s''')" % (self.method.rtype, result)))
|
|
elif self.method.rtype.startswith ("QMap") or self.method.rtype.startswith ("QValueList"):
|
|
dcop_add (params, args [i], self.argtypes [i])
|
|
else:
|
|
if not result:
|
|
dcop_add (s, "")
|
|
else:
|
|
dcop_add (s, result)
|
|
|
|
# use append because we want to return the replyType reference,
|
|
# not a new QCString
|
|
replyType.append (self.method.rtype)
|
|
|
|
# success
|
|
return True
|
|
|
|
def addMethod (self, signature, pymethod):
|
|
"""
|
|
add a method to the dict - makes it available to DCOP
|
|
signature - a string representing the C++ form of the method declaration
|
|
with arg names removed (eg
|
|
pymethod - the Python method corresponding to the method in signature
|
|
|
|
example:
|
|
def someMethod (a, b):
|
|
return str (a + b)
|
|
|
|
signature = "QString someMethod (int, int)"
|
|
pymethod = someMethod
|
|
self.addMethod (signature, pymethod)
|
|
|
|
note that in this case you could add a second entry:
|
|
|
|
self.addMethod ("QString someMethod (float, float)", someMethod)
|
|
|
|
pymethod can also be a class method, for example - self.someMethod or
|
|
someClass.someMethod. In the second case, someClass has to be an instance
|
|
of a class (perhaps SomeClass), not the class itself.
|
|
|
|
self.methods is a dict holding all of the methods exposed, indexed by
|
|
method signature. In the example above, the signature would be:
|
|
|
|
someMethod(QString,QString)
|
|
|
|
or everything but the return type, which is stored in the dict entry.
|
|
The dict entry is a DCOPExMeth instance.
|
|
"""
|
|
method = DCOPExMeth (signature, pymethod)
|
|
if method.sig:
|
|
self.methods [method.sig] = method
|
|
return method.sig != None
|
|
|
|
def matchMethod (self, meth):
|
|
# find the method in the dict if it's there
|
|
self.method = None
|
|
if meth in self.methods:
|
|
self.method = self.methods [meth]
|
|
return self.method != None
|
|
|
|
def functions (self):
|
|
# build the list of methods exposed for 'remoteFunctions' calls
|
|
# from the entries in the self.methods dict
|
|
funcs = DCOPObject.functions (self)
|
|
for func in self.methods.keys ():
|
|
funcs.append (" ".join ([self.methods [func].rtype, func]))
|
|
return funcs;
|
|
|
|
class DCOPExMeth:
|
|
"""
|
|
Encapsulates all of the method data - signature, arg list, return type
|
|
and corresponding Python method to be called
|
|
"""
|
|
def __init__ (self, method, pymethod):
|
|
self.pymethod = pymethod
|
|
if not self.parseMethod (method):
|
|
self.fcnname = self.sig = self.rtype = self.argtypes = None
|
|
|
|
def parseMethod (self, method):
|
|
# strip whitespace
|
|
method = str (DCOPClient.normalizeFunctionSignature (method)).replace (">", "> ")
|
|
|
|
# the return type (rtype) and signature (sig)
|
|
self.rtype, tail = method.split (" ", 1)
|
|
self.sig = tail
|
|
if not tail:
|
|
return False
|
|
self.rtype = self.rtype.strip ()
|
|
|
|
i = tail.find ("(")
|
|
if i < 1:
|
|
return False
|
|
|
|
# the name of the method
|
|
self.fcnname = tail [:i].strip () + "("
|
|
|
|
# the list of arg types
|
|
self.argtypes = []
|
|
args = tail [i + 1 : -1].split (",")
|
|
if args and args != [""]:
|
|
for arg in args:
|
|
self.argtypes.append (arg.strip ())
|
|
|
|
return True
|