diff options
Diffstat (limited to 'dcoppython')
37 files changed, 2935 insertions, 0 deletions
diff --git a/dcoppython/Makefile.am b/dcoppython/Makefile.am new file mode 100644 index 00000000..194e4691 --- /dev/null +++ b/dcoppython/Makefile.am @@ -0,0 +1,3 @@ + +SUBDIRS = shell lib + diff --git a/dcoppython/README b/dcoppython/README new file mode 100644 index 00000000..e5b8c3e8 --- /dev/null +++ b/dcoppython/README @@ -0,0 +1,66 @@ +PYTHON bindings for DCOP +======================== + +These are the new-style Python DCOP bindings. The way in which the bindings are +implemented has changed since KDE 3.1.1. + + +How they work +============= + +The code is divided into two parts: + +pcop.cpp - the C++ interface between Python and DCOP - generates shared library pcop.so + which can be imported by Python + +pydcop.py - the Python interface to pcop.cpp + +pcop.cpp includes a header file marshal_funcs.h, which is generated from +a data file called marshal_funcs.data by a converter script, gen_marshal_funcs.py + +marshal_funcs.data contains the basic code necessary to marshal and demarshal the different +types that DCOP can handle. For example, it codes how to convert a QString for use by Python +(in this case, a Python string) and the reverse - what the user may supply in Python when +DCOP requires a QString. In addition to the fundemental types, more complex QT classes are +coded, such as QRect (which converts to a Python tuple ( (x1,y1), (x2,y2) ) ). + +Documentation is auto-generated out of marshal_funcs.data, creating file marshal_funcs_doc.html, +which details how each DCOP type (e.g. QString, QRect, int, QCStringList) is represented in Python. + +In this implementation, each DCOP type is represented by a basic Python type - numeric, tuple, etc. +There are no "QT bindings" necessary. + +These bindings allow you to code Python to act as a DCOP client (querying and/or controlling +other DCOP applications), or as a DCOP server. This means that you can DCOP-enable Python applications +even if they are not QT based. + +If you want to use DCOP in the context of a Python QT application, then there are DCOP bindings included in +the PyQT and PyKDE bindings available from: + +http://www.riverbankcomputing.co.uk/ + +Examples +======== + +There are some example Python programs in the test directory. + +Known problems +============= + +There is currently a bug which means you must import both pcop and pydcop in your Python programs. +This means that a Python program using dcoppython must include: + +import pcop +import pydcop + +In that order. If you don't import pcop, a seg fault occurs when the interpreter exits. This, of course, will be +fixed once I find out what the hell's going on. + +Authors +======= + +The original Python DCOP bindings were written by Torben Weis (weis@kde.org). +The current implementation, based on Torben's worked, was written by Julian Rockey (kde@jrockey.com). +Julian is also the current maintainer. + + diff --git a/dcoppython/TODO b/dcoppython/TODO new file mode 100644 index 00000000..3da5fda9 --- /dev/null +++ b/dcoppython/TODO @@ -0,0 +1,7 @@ +dcoppython todo.. + + Enable/disable debugs + Check _object_ and _method_ naming convention + Signals + ASYNC + Threaded server diff --git a/dcoppython/configure.in.in b/dcoppython/configure.in.in new file mode 100644 index 00000000..2f645818 --- /dev/null +++ b/dcoppython/configure.in.in @@ -0,0 +1,9 @@ +KDE_CHECK_PYTHON(1.5) +if test -z "$LIBPYTHON" || test -z "$PYTHONINC"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE dcoppython" +fi + +AC_ARG_VAR([XSLTPROC]) +AC_ARG_VAR([PYTHON]) +AC_PATH_PROG([XSLTPROC],[xsltproc],[echo]) +AC_PATH_PROG([PYTHON],[python]) diff --git a/dcoppython/lib/Makefile.am b/dcoppython/lib/Makefile.am new file mode 100644 index 00000000..99dbac94 --- /dev/null +++ b/dcoppython/lib/Makefile.am @@ -0,0 +1,4 @@ + +pyt_DATA = pydcop.py +pytdir = $(PYTHONMODDIR) + diff --git a/dcoppython/lib/pydcop.py b/dcoppython/lib/pydcop.py new file mode 100644 index 00000000..0333b742 --- /dev/null +++ b/dcoppython/lib/pydcop.py @@ -0,0 +1,122 @@ +import pcop + +def registeredApplications(): + """Return a list of current DCOP registered applications.""" + return pcop.app_list() + +def apps(): + """Return a list of current DCOP registered applications.""" + return pcop.app_list() + +def anyAppCalled(appName): + """Return any application instance called appName, or None if none currently registered.""" + for app in apps(): + if appName==app or appName+'-'==app[:len(appName)+1]: + return DCOPApplication(app) + return None + +def registerAs(appid, addpid=1): + """Register the application with DCOP and return the ID. This is needed in order to receive DCOP requests.""" + return pcop.register_as(appid,addpid) + +def processEvents(): + """Process any waiting QT events, then return.""" + pcop.process_events() + +def connectDCOPSignal(sender,senderObj,signal,receiverObj,slot,vol=1): + """Connect a dcop signal""" + return pcop.connect_dcop_signal(sender,senderObj,signal,receiverObj,slot,vol) + +def disconnectDCOPSignal(sender,senderObj,signal,receiverObj,slot): + """Connect a dcop signal""" + return pcop.disconnect_dcop_signal(sender,senderObj,signal,receiverObj,slot) + +class DCOPApplication(object): + def __init__( self, name ): + self.name = name + + 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_": + return pcop.obj_list(self.name) + return DCOPObject( self.name, item ) + + def _object_(self, object): + return DCOPObject( self.name, object ) + +class DCOPObject(object): + def __init__( self, appname, name ): + self.appname = appname + self.name = name + + def __repr__( self ): + return "DCOPObject(%s,%s)" % ( self.appname, self.name ) + + def __str__( self ): + return "DCOPObject(%s,%s)" % ( self.appname, self.name ) + + 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 pcop.method_list( self.appname, self.name ) + return DCOPMethod( self.appname, self.name, item ) + + def _method_(self, method): + return DCOPMethod( self.appname, self.name, method ) + +class DCOPMethod(object): + def __init__( self, appname, objname, name ): + self.appname = appname + self.objname = objname + self.name = name + + def __repr__( self ): + return "DCOPMethod(%s,%s,%s)" % ( self.appname, self.objname, self.name ) + + def __str__( self ): + return "DCOPMethod(%s,%s,%s)" % ( self.appname, self.objname, self.name ) + + def __call__( self, *args ): + return pcop.dcop_call( self.appname, self.objname, self.name, args ) + +class DCOPServer(object): + def __init__( self, appid, addpid = 1): + self.app_id = pcop.register_as(appid, addpid) + + +class DCOPServerObject: + """Inherit from this class to DCOP enabled your object. + + Remember to call the base class constructor, and in your own constructor + you should called setMethods to set the methods to DCOP enable. + """ + + def __init__(self, objName=None): + """objName is the name of the object. If omitted, it will default to a hex + address. It is best to supply it.""" + if objName: + self.dcop_obj = pcop.create_dcop_object(self, objName) + else: + self.dcop_obj = pcop.create_dcop_object(self) + + def setMethods(self, methods): + """Set the method list for this object. + + methods is a list of tuple pairs. Each pair consists of the + method signature and the Python method that handles it. + + For example, setMethods([ ('QString cheeseType()', self.cheese_type), + ('void setGreatWines(bool perthPink, bool hobartMuddy, bool chateauChunder)') + """ + pcop.set_method_list(self.dcop_obj, methods) + diff --git a/dcoppython/shell/Makefile.am b/dcoppython/shell/Makefile.am new file mode 100644 index 00000000..3ec2906f --- /dev/null +++ b/dcoppython/shell/Makefile.am @@ -0,0 +1,25 @@ + +BUILT_SOURCES = marshal_funcs.h +CLEANFILES = marshal_funcs.h marshal_funcs_doc.html marshal_funcs_doc.xml + +doc: marshal_funcs_doc.html + +marshal_funcs.h marshal_funcs.xml: $(srcdir)/marshal_funcs.data + $(PYTHON) $(srcdir)/gen_marshal_code.py $(srcdir)/marshal_funcs.data marshal_funcs.h marshal_funcs_doc.xml + +marshal_funcs_doc.html: $(srcdir)/marshal_funcs_doc.xsl marshal_funcs_doc.xml + $(XSLTPROC) $(srcdir)/marshal_funcs_doc.xsl marshal_funcs_doc.xml >marshal_funcs_doc.html + +INCLUDES = $(PYTHONINC) $(all_includes) + +pythlib_LTLIBRARIES = pcop.la +pythlibdir = $(PYTHONMODDIR)/site-packages + +pcop_la_SOURCES = pcop.cpp marshaller.cpp importedmodules.cpp +pcop_la_LDFLAGS = $(all_libraries) -module -avoid-version +pcop_la_LIBADD = -lDCOP -lkdecore $(LIB_QT) + +noinst_HEADERS = pcop.h marshaller.h marshal_funcs.h importedmodules.h + + + diff --git a/dcoppython/shell/gen_marshal_code.py b/dcoppython/shell/gen_marshal_code.py new file mode 100644 index 00000000..73cb1fd0 --- /dev/null +++ b/dcoppython/shell/gen_marshal_code.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python +# Julian Rockey 2003 +# Generate marshall/demarshal functions from marshal_funcs.data file + +import sys +import re + +def cap_first(str): + """Capitalise first letter of string.""" + return str[0].upper() + str[1:] + +def set_method(attr): + """Return the name for a QT class setter method for an attribute.""" + return "set" + cap_first(attr) + +class DictMaker: + """Generate code for marshalling/demarshalling types using Python dictionaries.""" + + supported_types = ['string'] + re_dictmap = re.compile("%dict\-map(.*)") + re_dictmap_constructor = re.compile("%constructor (.+)") + + def __init__(self): + self.attr_list = [] + self.current_type = None + self.operation = None + self.constructor = None + + self.type_handlers = {} + for type in self.supported_types: + self.type_handlers[type] = (eval('self.handle_%s_marsh' % type), + eval('self.handle_%s_demarsh' % type)) + + def handle_string_marsh(self, attribute): + """Handle marshalling of string item from the dictionary.""" + return ["if (%s && !PyString_Check(%s)) return false;" % (attribute, attribute), + "if (%s) { qobj.%s(QString(PyString_AsString(%s)));" % (attribute, set_method(attribute), attribute), + "PyDict_DelItemString(dict,(char*)\"%s\"); } " % (attribute)] + + def handle_string_demarsh(self, attribute): + """Handle demarshalling of string items into the dictionary.""" + return ["PyObject *%s = PyString_FromString(qobj.%s().utf8().data() );" % (attribute ,attribute), + "PyDict_SetItemString(dict, (char*)\"%s\", %s);" % (attribute, attribute) + ] + + def pre_code_for(self, operation, attribute): + + if operation==MARSHAL: + return ["PyObject *%s = PyDict_GetItemString(dict,(char*)\"%s\");" % (attribute, attribute) ] + + return [] + + def post_code_for(self, operation, attribute): + return [] + + def code_for(self, operation, type, attribute): + if operation!=None and (type in self.type_handlers): + return self.pre_code_for(operation, attribute) + \ + self.type_handlers[type][not not operation](attribute) + \ + self.post_code_for(operation, attribute) + + return [] + + def set_current_type(self, current_type): + self.current_type = current_type + self.constructor = ""; + + def set_operation(self, operation): + if operation in [None, MARSHAL, DEMARSHAL]: + self.operation = operation + + def check_dictmap(self, line): + + if self.operation not in [MARSHAL,DEMARSHAL]: return [] + + m=self.re_dictmap_constructor.match(line) + if m: + self.constructor = m.groups()[0] + return [''] + + m=self.re_dictmap.match(line) + if not m: return [] + + if self.operation==MARSHAL: + result = ["{", + "if (!PyDict_Check(obj)) return false;", + "%s qobj%s;" % (self.current_type,self.constructor), + "PyObject *dict = PyDict_Copy(obj);" + ] + if self.operation==DEMARSHAL: + result = ["{", + "PyObject *dict = PyDict_New();", + "if (!dict) return NULL;", + "%s qobj%s;" % (self.current_type,self.constructor), + "(*str) >> qobj;" + ] + + if m.groups()[0].strip(): + self.attr_list = [tuple(x.split(':')) for x in m.groups()[0].strip().split(',') ] + + for attribute, type in self.attr_list: + result += self.code_for(self.operation, type, attribute) + + if self.operation==MARSHAL: + result += ["if (str) (*str) << qobj;", + "Py_DECREF(dict);", + "return true;", + "}" + ] + if self.operation==DEMARSHAL: + result += ["return dict;", + "}" + ] + + return result + +class DocType: + """A class to hold documentation information for each type.""" + + def __init__(self, type): + self.type = type + self.demarshal_as = None + self.as = [] + self.info = [] + + def add_as(self, as): + if self.demarshal_as == None: self.demarshal_as = as + self.as += [as] + + def add_info(self,info): + self.info += [info] + + def xml(self): + return ['<type dcoptype="%s">' % self.type, + ' <demarshal-as>%s</demarshal-as>' % self.demarshal_as] + \ + [' <marshal-as>%s</marshal-as>' % as for as in self.as ] + \ + [' <info>%s</info>' % info for info in self.info ] + \ + ['</type>'] + + +MARSHAL, DEMARSHAL, TOPYOBJ, FROMPYOBJ = 0,1,2,3 + +if len(sys.argv)!=4: + print "Use: gen_marshal_code.py <input file> <output file> <doc-xml-output file>" + raise RuntimeError + +nowt, in_name, code_name, doc_xml_name = tuple(sys.argv) + +##in_name, code_name, doc_xml_name = "marshal_funcs.data", "marshal_funcs.h", "marshal_funcs_doc.xml" + +gen_code_comments = ['/*', + ' * This code was generated by gen_marshal_code.py', + ' * Please do not modify, or it\'ll be overwritten!', + ' */', + ' ', + ] + +re_type = re.compile(r"type\: *([^\s]+).*") +re_marshDemarsh = re.compile("%% *(de)?marshal *.*") +re_tofromPyobj = re.compile("%% *(to|from)_pyobj *.*") +re_defaultCode = re.compile("%defaultcode *.*") +re_docInfo = re.compile("%doc *([^ ]+) *(.*)") + +in_file = open(in_name,"r") +code = [] + +types = {} +doc_types = {} +current_operation = None + +dict_maker = DictMaker() + +for l in in_file.readlines(): + l=l[:-1] + + # match a "type:" line + m=re_type.match(l) + if m: + current_type = m.groups()[0] + types[current_type]={} + doc_types[current_type] = DocType(current_type) + dict_maker.set_current_type(current_type) + continue + + m=re_docInfo.match(l) + if m: + doc_cmd, rest = m.groups() + if doc_cmd=="as": + doc_types[current_type].add_as(rest) + if doc_cmd=="info": + doc_types[current_type].add_info(rest) + continue + + # match a "%% marshal" or "%% demarshal" line + m=re_marshDemarsh.match(l) + if m: + if m.groups()[0]: + current_operation = DEMARSHAL + code.append("PyObject *demarshal_" + current_type + \ + "(QDataStream *str)") + else: + current_operation = MARSHAL + code.append("bool marshal_" + current_type + \ + "(PyObject *obj, QDataStream *str)") + dict_maker.set_operation(current_operation) + continue + + m=re_tofromPyobj.match(l) + if m: + if m.groups()[0]=='to': + current_operation = TOPYOBJ + code += ["PyObject *toPyObject_%s(%s val)" % (current_type,current_type)] + elif m.groups()[0]=='from': + current_operation = FROMPYOBJ + code += ["%s fromPyObject_%s(PyObject *obj, bool *ok)" % (current_type,current_type)] + continue + + + if l.strip()=='%%': + current_operation = None + dict_maker.set_operation(current_operation) + + if current_operation!=None: + types[current_type][current_operation]=1 + + dict_code = dict_maker.check_dictmap(l) + if dict_code: + code += dict_code + continue + + m=re_defaultCode.match(l) + if m: + if current_operation==MARSHAL: + code += [ + "{", + " bool ok;", + " %s qobj=fromPyObject_%s(obj,&ok);" % (current_type,current_type), + " if (ok && str) (*str) << qobj;", + " return ok;", + "}" + ] + continue + if current_operation==DEMARSHAL: + code += [ + "{", + " %s qobj;" % current_type, + " (*str) >> qobj;", + " return toPyObject_%s(qobj);" % current_type, + "}" + ] + continue + + code.append(l) + +in_file.close() + +code.append("void Marshaller::initFuncs() {") +for t in types: + if MARSHAL in types[t]: + code.append("m_marsh_funcs[\"" + t + "\"]=marshal_" + t + ";") + if DEMARSHAL in types[t]: + code.append("m_demarsh_funcs[\"" + t + "\"]=demarshal_" + t + ";") +code.append("}") + +out_file = open(code_name,"w") +out_file.writelines([x + '\n' for x in gen_code_comments]) +out_file.writelines([x + '\n' for x in code]) +out_file.close() + +xml_file = file(doc_xml_name,"w") +print >>xml_file, '<?xml version="1.0" ?>' +print >>xml_file, '<!-- This file was auto-generated by gen_marshal_code.py. Changes will be lost! -->' +print >>xml_file, "<types>" +[ [xml_file.write(x+"\n") for x in doc.xml()] for doc in doc_types.values() ] # silly one-liner +print >>xml_file, "</types>" +xml_file.close() diff --git a/dcoppython/shell/importedmodules.cpp b/dcoppython/shell/importedmodules.cpp new file mode 100644 index 00000000..4b2a23bb --- /dev/null +++ b/dcoppython/shell/importedmodules.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey (kde@jrockey.com) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "importedmodules.h" + +#include <kdebug.h> + +namespace PythonDCOP { + + ImportedModules *ImportedModules::m_instance = NULL; + + ImportedModules::ImportedModules() : m_dcop_module(NULL) + { + m_dcop_module = PyImport_ImportModule( (char*)"pydcop" ); + if ( !m_dcop_module ) + kdDebug(70001) << "Could not import pydcop module" << endl; + } + + ImportedModules::~ImportedModules() + { + } + + PyObject* ImportedModules::createDCOPObject( const char* appname, const char* objname ) + { + if ( !m_dcop_module ) + return 0; + + PyObject* dict = PyModule_GetDict( m_dcop_module ); + if ( !dict ) + return 0; + + PyObject* cl = PyDict_GetItemString( dict, (char*)"DCOPObject" ); + if ( !cl ) + return 0; + + PyObject* args = PyTuple_New( 2 ); + PyTuple_SetItem( args, 0, PyString_FromString( appname ) ); + PyTuple_SetItem( args, 1, PyString_FromString( objname ) ); + + return PyObject_CallObject( cl, args ); + } + +} diff --git a/dcoppython/shell/importedmodules.h b/dcoppython/shell/importedmodules.h new file mode 100644 index 00000000..9d47f617 --- /dev/null +++ b/dcoppython/shell/importedmodules.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey (kde@jrockey.com) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef __importedmodules_h__ +#define __importedmodules_h__ + +#include <Python.h> + +namespace PythonDCOP { + + /** + * Manages imported Python modules. + */ + class ImportedModules { + public: + ImportedModules(); + ~ImportedModules(); + PyObject *createDCOPObject( const char* appname, const char* objname ); + PyObject *dcop_module() const { return m_dcop_module; } + + static ImportedModules *instance() { return m_instance; } + static void setInstance(ImportedModules *instance) { m_instance = instance; } + + private: + PyObject *m_dcop_module; + static ImportedModules *m_instance; + }; + +} + +#endif diff --git a/dcoppython/shell/marshal_funcs.data b/dcoppython/shell/marshal_funcs.data new file mode 100644 index 00000000..abb3a43e --- /dev/null +++ b/dcoppython/shell/marshal_funcs.data @@ -0,0 +1,597 @@ +// This file contains the C++ code necessary marshal and demarshal +// all the _simple_ types that dcoppython can understand. +// "Simple" types are types that do not contain other types. +// So, int and QString are simple types; QDict, QMap and QStringList are not. +// This file is processed by gen_marshal_code.py to produce a header +// file, which is included by marshaller.cpp +// +// Marshalling: +// The code in the "marshal" section has the following variables available: +// PyObject * obj; // the object to marshal +// QDataStream *str; // the stream to marshal to +// The function should return true if the object can be marshalled. +// str may be NULL. If so, the function should ignore the actually marshalling +// and merely return true or false, depending on whether the object _could_ +// be marshalled. +// +// Demarshalling: +// The code in the "demarshal" section has the following variables available: +// QDataStream *str; // the stream to demarshal from +// The function should return a PyObject* which is a reference to the +// newly created object. Ownership of the reference should be passed to +// the caller. The function can return null if for any reason it +// could not demarshal. + +type: void +%% marshall +{ + Q_UNUSED(str); // stop warnings + Q_UNUSED(obj); + return true; +} +%% demarshal +{ + Q_UNUSED(str); // stop warnings + Py_INCREF(Py_None); + return Py_None; +} + +type: bool +%doc as int b (1=True, 2=False) +%doc info Any Python object is converted to bool by the standard Python truth test. +%% from_pyobj + { + *ok=true; + return PyObject_IsTrue(obj); + } +%% to_pyobj + { + return PyInt_FromLong(val ? 1 : 0); + } +%% marshal + { + if (str) { + bool ok; + bool b = fromPyObject_bool(obj,&ok); + (*str) << (Q_INT8)b; + } + return true; + } +%% demarshal + { + Q_INT8 i; + (*str) >> i; + return toPyObject_bool(i!=0); + } +%% + +type:int +%doc as int i +%% marshal + { + if (!PyInt_Check(obj)) return false; + if (str) { + (*str) << (Q_INT32)PyInt_AsLong(obj); + } + return true; + } +%% demarshal + { + Q_INT32 i; + (*str) >> i; + return PyInt_FromLong( (long)i ); + } +%% + +type:uint +%doc as int i +%% marshal + { + if (!PyInt_Check(obj)) return false; + if (str) { + (*str) << (Q_INT32)PyInt_AsLong(obj); + } + return true; + } +%% demarshal + { + Q_INT32 i; + (*str) >> i; + return PyInt_FromLong( (long)i ); + } +%% + +type:double +%doc as float i +%% marshal + { + if (!PyFloat_Check(obj)) return false; + if (str) { + (*str) << PyFloat_AsDouble(obj); + } + return true; + } +%% demarshal + { + double d; + (*str) >> d; + return PyFloat_FromDouble(d); + } +%% + +type:uchar +%doc as str c +%doc as int c +%% marshal + { + if (PyString_Check(obj) && PyString_Size(obj)==1) { + if (str) { + char *c = PyString_AsString(obj); + (*str) << (*c); + } + return true; + } + + if (PyInt_Check(obj)) { + if (str) { + long l = PyInt_AsLong(obj); + Q_UINT8 c = (Q_UINT8)(l & 0xff); + (*str) << c; + } + return true; + } + + return false; + } +%%demarshal + { + Q_UINT8 c; + (*str) >> c; + return PyString_FromStringAndSize((const char *)(&c),1); + } +%% + +type:char +%doc as int c +%% marshal + { + if (PyInt_Check(obj)) { + if (str) { + long l = PyInt_AsLong(obj); + Q_INT8 c = (Q_INT8)(l & 0xff); + (*str) << c; + } + return true; + } + + return false; + } +%%demarshal + { + Q_INT8 c; + (*str) >> c; + return PyInt_FromLong((long)c); + } +%% + + +type:QByteArray +%% marshal + { + PyBufferProcs *pb = obj->ob_type->tp_as_buffer; + + if ( pb && pb->bf_getreadbuffer && pb->bf_getsegcount ) + { + // Get the number of buffer segments + int seg_count = (pb->bf_getsegcount)(obj, 0); + + if ( seg_count != 1 ) + // Can't handle more (or less) than 1 buffer segment + // at the moment + return false; + + // Get buffer size and data + void *data; + int size; + + if ( (size = (pb->bf_getreadbuffer)(obj, 0, &data)) < 0 ) + return false; + + if (str) { + QByteArray a; + a.setRawData( (const char*)data, size ); + (*str) << a; + a.resetRawData( (const char*)data, size ); + } + + return true; + } + else + // obj does not implement the buffer interface + return false; + } +%% demarshal + { + // Demarshal to a writable buffer object + QByteArray a; + (*str) >> a; + + uint size = a.size(); + char *data = a.data(); + + // Create a new buffer object and copy the data. + // Don't use PyBuffer_FromMemory() and the likes since + // that wouldn't give correct allocation and deallocation. + + PyObject *buffer_obj = PyBuffer_New( size ); + + if ( !buffer_obj ) + return NULL; + + PyBufferProcs *pb = buffer_obj->ob_type->tp_as_buffer; + + void *buffer_data; + + (pb->bf_getwritebuffer)( buffer_obj, 0, &buffer_data ); + + for ( uint i = 0; i < size; i++ ) + ((char*)buffer_data)[i] = data[i]; + + return buffer_obj; + } +%% + +type:QString +%doc as str s +%% marshal + { + if (!PyString_Check(obj)) return false; + if (str) { + QString s( PyString_AsString(obj) ); + (*str) << s; + } + return true; + } +%% demarshal + { + QString s; + (*str) >> s; + return PyString_FromString( s.utf8().data() ); + } +%% + +type:QCString +%doc as str s +%% marshal + { + if (!PyString_Check(obj)) return false; + if (str) { + QCString s( PyString_AsString(obj) ); + (*str) << s; + } + return true; + } +%% demarshal + { + QCString s; + (*str) >> s; + return PyString_FromString( s.data() ); + } +%% + +type:QRect +%doc as ( (int x1, int y1), (int x2, int y2) ) +%doc as ( int x1, int y1, int x2, int y2 ) +%% from_pyobj +{ + int xp1, yp1, xp2, yp2; + QRect r; + *ok=false; + if (!PyTuple_Check(obj)) return r; + if (!PyArg_ParseTuple(obj, (char*)"(ii)(ii)", &xp1, &yp1, &xp2, &yp2) && + !PyArg_ParseTuple(obj, (char*)"iiii", &xp1, &yp1, &xp2, &yp2)) + return r; + r.setCoords( xp1, yp1, xp2, yp2 ); + *ok=true; + return r; +} +%% to_pyobj +{ + int xp1, yp1, xp2, yp2; + val.coords(&xp1,&yp1,&xp2,&yp2); + return Py_BuildValue((char*)"(ii)(ii)", xp1, yp1, xp2, yp2); +} + +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + +type:QPoint +%doc as (int x, int y) +%% from_pyobj +{ + int x,y; + QPoint p; + *ok=false; + if (!PyTuple_Check(obj)) return p; + if (!PyArg_ParseTuple(obj, (char*)"ii", &x, &y)) + return p; + p.setX(x); + p.setY(y); + *ok=true; + return p; +} +%% to_pyobj +{ + return Py_BuildValue((char*)"ii", val.x(), val.y() ); +} +%% marshall +%defaultcode +%% demarshall +%defaultcode +%% + +type:QSize +%doc as (int width, int height) +%% from_pyobj +{ + int w,h; + QSize sz; + *ok=false; + if (!PyTuple_Check(obj)) return sz; + if (!PyArg_ParseTuple(obj, (char*)"ii", &w, &h)) + return sz; + sz.setWidth(w); + sz.setHeight(h); + *ok=true; + return sz; +} +%% to_pyobj +{ + return Py_BuildValue((char*)"ii", val.width(), val.height() ); +} +%% marshall +%defaultcode +%% demarshall +%defaultcode +%% + +type:QColor +%doc as (int red, int green, int blue) +%% from_pyobj +{ + int r,g,b; + QColor c; + *ok=false; + if (!PyTuple_Check(obj)) return c; + if (!PyArg_ParseTuple(obj, (char*)"iii", &r, &g, &b)) + return c; + c.setRgb(r,g,b); + *ok=true; + return c; +} +%% to_pyobj +{ + return Py_BuildValue((char*)"iii", val.red(), val.green(), val.blue() ); +} +%% marshall +%defaultcode +%% demarshall +%defaultcode +%% + +type:QPointArray +%doc as [ (int x, int y), (int x, int y), (int x, int y), ... ] +%% from_pyobj +{ + *ok=false; + if (!PyList_Check(obj)) return QPointArray(); + int size = PyList_Size(obj); + QPointArray pa(size); + for(int c=0;c<size;c++) { + QPoint p = fromPyObject_QPoint(PyList_GetItem(obj,c), ok); + if (!*ok) return false; + pa.setPoint(c,p); + } + *ok=true; + return pa; +} +%% to_pyobj +{ + PyObject *obj = PyList_New(val.size()); + if (!obj) return NULL; + for(uint c=0;c<val.size();c++) { + PyObject *tuple = toPyObject_QPoint( val.point(c) ); + PyList_SetItem(obj, c, tuple); +// Py_DECREF(tuple); + } + return obj; +} +%% marshall +%defaultcode +%% demarshall +%defaultcode +%% + +type:QDate +%doc as (int year, int month, int day) +%% from_pyobj +{ + *ok=false; + if (!PyTuple_Check(obj)) return QDate(); + int y,m,d; + if (!PyArg_ParseTuple(obj, (char*)"iii", &y, &m, &d)) + return QDate(); + *ok=true; + return QDate(y,m,d); +} +%% to_pyobj +{ + return Py_BuildValue((char*)"iii", val.year(), val.month(), val.day() ); +} +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + +type:QTime +%doc as (int hour, int minute, int second=0, int millisecond=0) +%% from_pyobj +{ + *ok=false; + if (!PyTuple_Check(obj)) return QTime(); + int h,m,s=0,ms=0; + if (!PyArg_ParseTuple(obj, (char*)"ii|ii", &h, &m, &s, &ms)) + return QTime(); + *ok=true; + return QTime(h,m,s,ms); +} +%% to_pyobj +{ + return Py_BuildValue((char*)"iiii", val.hour(), val.minute(), val.second(), val.msec() ); +} +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + +type:QDateTime +%doc as ( (int year, int month, int day), (int hour, int minute, int second=0, int millsecond=0) ) +%doc as long unixDate +%% from_pyobj +{ + *ok=false; + + if (PyLong_Check(obj)) { + *ok=true; + QDateTime dt; + dt.setTime_t( (uint)PyLong_AsLong(obj) ); + return dt; + } + + if (PyInt_Check(obj)) { + *ok=true; + QDateTime dt; + dt.setTime_t( (uint)PyInt_AsLong(obj) ); + return dt; + } + + PyObject *date_tuple, *time_tuple; + if (PyArg_ParseTuple(obj, (char*)"OO", &date_tuple, &time_tuple)) { + QDateTime dt; + dt.setTime( fromPyObject_QTime(time_tuple, ok) ); + if (*ok) dt.setDate( fromPyObject_QDate(date_tuple, ok) ); + return dt; + } + + return QDateTime(); +} +%% to_pyobj +{ + PyObject *date_tuple = toPyObject_QDate( val.date() ); + PyObject *time_tuple = toPyObject_QTime( val.time() ); + return Py_BuildValue((char*)"OO", date_tuple, time_tuple ); +} +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + +type:KURL +%doc as str url +%% from_pyobj +{ + *ok=false; + if (!PyString_Check(obj)) return KURL(); + *ok=true; + return KURL( QString(PyString_AsString(obj)) ); +} +%% to_pyobj +{ + return PyString_FromString( val.prettyURL().utf8().data() ); +} +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + +type:DCOPRef +%% from_pyobj +{ + if (PyInstance_Check(obj) && + PyObject_HasAttrString(obj, (char*)"appname") && + PyObject_HasAttrString(obj, (char*)"name")) { + PyObject *appname = PyObject_GetAttrString(obj, (char*)"appname"); + PyObject *name = PyObject_GetAttrString(obj, (char*)"name"); + if (PyString_Check(appname) && PyString_Check(name)) { + char *c_appname = PyString_AsString(appname); + char *c_name = PyString_AsString(name); + DCOPRef ref; + ref.setRef(QCString(c_appname), QCString(c_name) ); + Py_DECREF(appname); + Py_DECREF(name); + *ok=true; + return ref; + } + Py_DECREF(appname); + Py_DECREF(name); + } + *ok=false; + return DCOPRef(); +} +%% to_pyobj +{ + if (val.isNull()) { + Py_INCREF(Py_None); + return Py_None; + } + return ImportedModules::instance()->createDCOPObject(val.app(), val.object() ); +} +%% marshal +%defaultcode +%% demarshal +%defaultcode +%% + + +// type:DCOPRef +// %doc as (str app, str obj, str type) +// %doc as (str app, str obj) +// %% from_pyobj +// { +// *ok=false; +// char *dcopref_app=NULL, *dcopref_obj=NULL, *dcopref_type=NULL; +// if (PyArg_ParseTuple(obj,(char*)"ss|s", &dcopref_app, &dcopref_obj, &dcopref_type)) { +// *ok=true; +// if (dcopref_type) { +// DCOPRef dr(QCString(dcopref_app), QCString(dcopref_obj), QCString(dcopref_type)); +// return dr; +// } +// DCOPRef dr(QCString(dcopref_app), QCString(dcopref_obj)); +// return dr; +// } +// return DCOPRef(); +// } +// %% to_pyobj +// { +// return Py_BuildValue((char*)"sss", val.app().data(), val.obj().data(), val.type().data() ); +// } +// %% marshal +// %defaultcode +// %% demarshal +// %defaultcode +// %% + +// type:QFont +// %% marshal +// %constructor ("default") +// %dict-map family:string,rawName:string +// %% demarshal +// %dict-map +// %% diff --git a/dcoppython/shell/marshal_funcs_doc.xsl b/dcoppython/shell/marshal_funcs_doc.xsl new file mode 100644 index 00000000..491c188c --- /dev/null +++ b/dcoppython/shell/marshal_funcs_doc.xsl @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + + <xsl:output method="html"/> + + <xsl:template match="/"> + <html> + <body> + <xsl:apply-templates/> + </body> + </html> + </xsl:template> + + <xsl:template match="types"> + <h1>DCOPPython supported types</h1> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template match="type"> + <h2><xsl:value-of select="@dcoptype"/></h2> + <xsl:apply-templates select="demarshal-as"/> + <div>Argument of form:</div> + <div style="margin-left: 2cm"> + <xsl:for-each select="marshal-as"> + <b><xsl:value-of select="."/></b> + </xsl:for-each> + </div> + <xsl:apply-templates select="info"/> + </xsl:template> + + <xsl:template match="demarshal-as"> + <div>Returns as: <b><xsl:apply-templates/></b></div> + </xsl:template> + + <xsl:template match="info"> + <div><xsl:apply-templates/></div> + </xsl:template> + +</xsl:stylesheet> diff --git a/dcoppython/shell/marshaller.cpp b/dcoppython/shell/marshaller.cpp new file mode 100644 index 00000000..f2dd4d03 --- /dev/null +++ b/dcoppython/shell/marshaller.cpp @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey * + * linux@jrockey.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "marshaller.h" + +#include "pcop.h" +#include "importedmodules.h" + +#include <qdatastream.h> + +#include <qrect.h> +#include <qfont.h> +#include <qcolor.h> +#include <qpointarray.h> +#include <qdatetime.h> +#include <dcopref.h> + +#include <kurl.h> + +#if PY_VERSION_HEX < 0x02050000 +typedef int Py_ssize_t; +#endif + +namespace PythonDCOP { + +#include "marshal_funcs.h" + + Marshaller::Marshaller() + { + initFuncs(); + } + + Marshaller::~Marshaller() + { + } + + bool Marshaller::marsh_private(const PCOPType &type, + PyObject *obj, + QDataStream *str) const + { + + QString ty = type.type(); + + if (ty=="QStringList") + return marshalList(PCOPType("QString"), obj, str); + if (ty=="QCStringList") + return marshalList(PCOPType("QCString"), obj, str); + if (ty=="QValueList" && type.leftType()) + return marshalList(*type.leftType(), obj, str); + if (ty=="QMap" && type.leftType() && type.rightType()) + return marshalDict(*type.leftType(), *type.rightType(), obj, str); + + if (!m_marsh_funcs.contains(ty)) return false; + return m_marsh_funcs[ty](obj,str); + } + + PyObject *Marshaller::demarsh_private(const PCOPType &type, + QDataStream *str) const + { + QString ty = type.type(); + + if (ty=="QStringList") + return demarshalList(PCOPType("QString"), str); + if (ty=="QCStringList") + return demarshalList(PCOPType("QCString"), str); + if (ty=="QValueList" && type.leftType()) + return demarshalList(*type.leftType(), str); + if (ty=="QMap" && type.leftType() && type.rightType()) + return demarshalDict(*type.leftType(), *type.rightType(), str); + + if (!m_demarsh_funcs.contains(ty)) { + Py_INCREF(Py_None); + return Py_None; + } + + PyObject *result = m_demarsh_funcs[ty](str); + if (!result) { + Py_INCREF(Py_None); + return Py_None; + } + + return result; + } + + bool Marshaller::marshalList(const PCOPType &list_type, + PyObject *obj, + QDataStream *str) const { + if (!PyList_Check(obj)) return false; + + int count = PyList_Size(obj); + + for(int c=0;c<count;c++) + if (!list_type.isMarshallable( PyList_GetItem(obj,c) ) ) + return false; + + if (str) { + (*str) << (Q_INT32)count; + for(int c=0; c<count; c++) + list_type.marshal( PyList_GetItem(obj,c), *str ); + } + + return true; + } + + PyObject *Marshaller::demarshalList(const PCOPType &list_type, + QDataStream *str) const { + Q_UINT32 count; + (*str) >> count; + + PyObject *obj = PyList_New(count); + for(Q_UINT32 c=0;c<count;c++) { + PyList_SetItem(obj, c, list_type.demarshal(*str)); + } + return obj; + } + + bool Marshaller::marshalDict(const PCOPType &key_type, + const PCOPType &value_type, + PyObject *obj, + QDataStream *str) const { + if (!PyDict_Check(obj)) return false; + + + Py_ssize_t c=0; + PyObject *key, *val; + while (PyDict_Next(obj, &c, &key, &val)==1) + if (!key_type.isMarshallable(key) || + !value_type.isMarshallable(val)) + return false; + + if (str) { + Q_INT32 count = (Q_INT32)PyDict_Size(obj); + (*str) << count; + c=0; + while (PyDict_Next(obj, &c, &key, &val)==1) { + key_type.marshal(key,*str); + value_type.marshal(val,*str); + } + } + return true; + } + + PyObject *Marshaller::demarshalDict(const PCOPType &key_type, + const PCOPType &value_type, + QDataStream *str) const { + PyObject *obj = PyDict_New(); + Q_INT32 count; + (*str) >> count; + for(Q_INT32 c=0;c<count;c++) { + PyObject *key = key_type.demarshal(*str); + PyObject *value = value_type.demarshal(*str); + PyDict_SetItem(obj,key,value); + } + return obj; + } + + + Marshaller *Marshaller::m_instance = new Marshaller; + + +} + diff --git a/dcoppython/shell/marshaller.h b/dcoppython/shell/marshaller.h new file mode 100644 index 00000000..920afb05 --- /dev/null +++ b/dcoppython/shell/marshaller.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey * + * linux@jrockey.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef __marshaller_h__ +#define __marshaller_h__ + +#include <qmap.h> +#include <Python.h> +#include <qstring.h> + +class QDataStream; + +namespace PythonDCOP { +// class Marshaller; + class PCOPType; + + class Marshaller { + public: + Marshaller(); + ~Marshaller(); + bool marshal(const PCOPType &type, PyObject *obj, QDataStream &str) const + { return marsh_private(type,obj,&str); } + bool canMarshal(const PCOPType &type, PyObject *obj) const + { return marsh_private(type,obj,NULL); } + bool marshalList(const PCOPType &list_type, PyObject *obj, QDataStream *str) const; + PyObject *demarshal(const PCOPType &type, QDataStream &str) const + { return demarsh_private(type, &str); } + PyObject *demarshalList(const PCOPType &list_type, QDataStream *str) const; + bool marshalDict(const PCOPType &key_type, const PCOPType &value_type, + PyObject *obj, QDataStream *str) const; + PyObject *demarshalDict(const PCOPType &key_type, + const PCOPType &value_type, + QDataStream *str) const; + static Marshaller *instance() { return m_instance; } + protected: + QMap<QString,bool(*)(PyObject*,QDataStream*)> m_marsh_funcs; + QMap<QString,PyObject*(*)(QDataStream*)> m_demarsh_funcs; + + static Marshaller *m_instance; + + void initFuncs(); + private: + bool marsh_private(const PCOPType &type, + PyObject *obj, + QDataStream *str) const; + PyObject *demarsh_private(const PCOPType &type, + QDataStream *str) const; + + + + }; + +// bool marshall_bool(PyObject *obj, QDataStream *str); +// bool marshall_int(PyObject *obj, QDataStream *str); +// bool marshall_uint(PyObject *obj, QDataStream *str); +// bool marshall_double(PyObject *obj, QDataStream *str); +// bool marshall_QByteArray(PyObject *obj, QDataStream *str); +// bool marshall_QString(PyObject *obj, QDataStream *str); +// bool marshall_QCString(PyObject *obj, QDataStream *str); + + +} + +#endif diff --git a/dcoppython/shell/pcop.cpp b/dcoppython/shell/pcop.cpp new file mode 100644 index 00000000..d7c4adc6 --- /dev/null +++ b/dcoppython/shell/pcop.cpp @@ -0,0 +1,770 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey (linux@jrockey.com) * + * Original code by Torben Weis * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + + +#include "pcop.h" + +#include <kdebug.h> + +#include <qapplication.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qstring.h> + +#include <dcopclient.h> + +#include <assert.h> + +#include "marshaller.h" +#include "importedmodules.h" + +namespace PythonDCOP { + + PCOPObject::PCOPObject(PyObject *py_obj) : + DCOPObject(), m_py_obj(py_obj) + { + m_methods.setAutoDelete(true); + } + + PCOPObject::PCOPObject(PyObject *py_obj, const char *objid) : + DCOPObject(QCString(objid)), m_py_obj(py_obj) + { + m_methods.setAutoDelete(true); + } + + PCOPObject::~PCOPObject() + { + } + + bool PCOPObject::process(const QCString &fun, const QByteArray &data, + QCString& replyType, QByteArray &replyData) + { + bool result = py_process(fun,data,replyType,replyData); + if (PyErr_Occurred()) { + kdDebug(70001) << "Error! About to print..." << endl; + PyErr_Print(); + kdDebug(70001) << "About to clear..." << endl; + PyErr_Clear(); + kdDebug(70001) << "Error handled." << endl; + } + return result; + } + + bool PCOPObject::py_process(const QCString &fun, const QByteArray &data, + QCString& replyType, QByteArray &replyData) + { + + kdDebug(70001) << "PCOPObject::process - fun=" << fun << " replyType=" << replyType << endl; + + PCOPMethod *meth = matchMethod(fun); + if (!meth) { + kdDebug(70001) << "Could not match method name" << endl; + } + + if (meth) { + + kdDebug(70001) << "m_py_obj=" << m_py_obj << " meth->name=" << meth->name() << " meth->name.data=" << meth->name().data() << endl; + if (meth->name().isNull()) { kdDebug(70001) << "meth name is null" << endl; return false; } +// if (!PyObject_HasAttrString(m_py_obj, meth->name().data())) { +// kdDebug(70001) << "Method registered, but no python method found" << endl; +// return false; +// } + + QDataStream str_arg(data, IO_ReadOnly); + PyObject *args = PyTuple_New( meth->paramCount() ); + for(int c=0;c<meth->paramCount();c++) { + kdDebug(70001) << "Demarshalling type: " << meth->param(c)->signature() << endl; + PyObject *arg = meth->param(c)->demarshal(str_arg); + if (!arg) { + kdDebug(70001) << "Failed to demarshall an argument" << endl; + return false; + } + PyTuple_SetItem(args, c, arg ); + } + + kdDebug(70001) << "args is " << PyTuple_Size(args) << " long" << endl; + +// PyObject *method = PyObject_GetAttrString(m_py_obj, meth->name().data() ); + PyObject *method = meth->pythonMethod(); + if (!PyCallable_Check(method)) { + kdDebug(70001) << "Expected a callable object, but didn't get one!" << endl; + return false; + } + +// PyObject *function = PyMethod_Function(method); +// PyObject *self = PyMethod_Self(method); +// Py_INCREF(self); +// PyTuple_SetItem(args, 0, self ); +// PyObject *result = PyObject_CallObject(function, args); + +// Py_DECREF(method); + if (PyMethod_Self(method)==NULL) + kdDebug(70001) << "Warning: self is null!" << endl; + + kdDebug(70001) << "About to call object.." << endl; + PyObject *result = PyObject_CallObject(method, args); + kdDebug(70001) << "Finished calling object." << endl; + + if (result) { + replyType = meth->type()->signature(); + PCOPType repl(replyType); + if (repl.isMarshallable(result)) { + QDataStream str_repl(replyData, IO_WriteOnly); + repl.marshal(result,str_repl); + Py_DECREF(result); + return true; + } else { + Py_DECREF(result); + kdDebug(70001) << "Result of python method was not marshallable into " << replyType << endl; + return false; + } + } + else { + kdDebug(70001) << "null result from python method call" << endl; + return false; + } + + } + + return DCOPObject::process(fun,data,replyType,replyData); + + } + + bool PCOPObject::setMethodList(QAsciiDict<PyObject> meth_list) { + bool ok = true; + + for(QAsciiDictIterator<PyObject> it(meth_list); + it.current(); ++it) { + + PCOPMethod *meth = NULL; + if (ok) { + meth = new PCOPMethod(QCString(it.currentKey())); + + if (!meth || !meth->setPythonMethod(it.current())) { + if (meth) delete meth; + meth=NULL; + m_methods.clear(); + ok=false; + } + + } + +// Py_DECREF(it.current()); + if (meth) m_methods.insert(meth->signature(),meth); + } + + return ok; + } + + QCStringList PCOPObject::functions() { + QCStringList funcs = DCOPObject::functions(); + for(QAsciiDictIterator<PCOPMethod> it(m_methods); + it.current(); ++it) { + PCOPMethod *meth = it.current(); + QCString func = meth->type()->signature(); + func += ' '; + func += meth->signature(); + funcs << func; + } + return funcs; + } + + /** + * For testing + */ + PyObject *PCOPObject::methodList() { + PyObject *result = PyList_New(m_methods.count()); + int c=0; + for(QAsciiDictIterator<PCOPMethod> it(m_methods); + it.current(); ++it, ++c) { + PyObject *tuple = PyTuple_New(2); + PyList_SetItem(result, c, tuple); + PyTuple_SetItem(tuple, 0, PyString_FromString(it.currentKey() ) ); + PyTuple_SetItem(tuple, 1, it.current()->pythonMethod() ); + } + return result; + } + + PCOPMethod *PCOPObject::matchMethod(const QCString &fun) { + return m_methods.find(fun); + } + + + PCOPType::PCOPType( const QCString& type ) + { + m_leftType = NULL; + m_rightType = NULL; + + int pos = type.find( '<' ); + if ( pos == -1 ) + { + m_type = type; + return; + } + + int pos2 = type.findRev( '>' ); + if ( pos2 == -1 ) + return; + + m_type = type.left( pos ); + + // There may be no more than 2 types in the bracket + int komma = type.find( ',', pos + 1 ); + if ( komma == -1 ) + { + m_leftType = new PCOPType( type.mid( pos + 1, pos2 - pos - 1 ) ); + } + else + { + m_leftType = new PCOPType( type.mid( pos + 1, komma - pos - 1 ) ); + m_rightType = new PCOPType( type.mid( komma + 1, pos2 - komma - 1 ) ); + } + } + + PCOPType::~PCOPType() + { + if (m_leftType) delete m_leftType; + if (m_rightType) delete m_rightType; + } + + QCString PCOPType::signature() const + { + QCString str = m_type; + if ( m_leftType ) + { + str += "<"; + str += m_leftType->signature(); + + if ( m_rightType ) + { + str += ","; + str += m_rightType->signature(); + } + + str += ">"; + } + + return str; + } + + bool PCOPType::marshal( PyObject* obj, QDataStream& str ) const + { + return Marshaller::instance()->marshal(*this, obj, str); + } + + bool PCOPType::isMarshallable( PyObject *obj ) const + { + return Marshaller::instance()->canMarshal(*this, obj); + } + + PyObject* PCOPType::demarshal( QDataStream& str ) const + { + return Marshaller::instance()->demarshal(*this, str); + } + + PCOPMethod::PCOPMethod( const QCString& signature ) : + m_py_method(NULL) + { + + m_type = 0; + m_params.setAutoDelete( TRUE ); + + // Find the space that separates the type from the name + int k = signature.find( ' ' ); + if ( k == -1 ) + return; + + // Create the return type from the string + m_type = new PCOPType( signature.left( k ) ); + + // Find the brackets + int i = signature.find( '(' ); + if ( i == -1 ) + return; + int j = signature.find( ')' ); + if ( j == -1 ) + return; + + // Extract the name + m_name = signature.mid( k + 1, i - k - 1 ); + + // Strip the parameters + QCString p = signature.mid( i + 1, j - i - 1 ).stripWhiteSpace(); + + if ( !p.isEmpty() ) { + // Make the algorithm terminate + p += ","; + + // Iterate over the parameters + int level = 0; + int start = 0; + int len = p.length(); + for( int i = 0; i < len; ++i ) + { + // Found a comma? Then we reached the end of a parameter + if ( p[i] == ',' && level == 0 ) + { + // Find the space that separates name from type. + int space = p.find( ' ', start ); + + if ( space == -1 || space > i ) // unnamed parameter + space = i; + + PCOPType* type = new PCOPType( p.mid( start, space - start ) ); + m_params.append( type ); + + // Start of the next parameter + start = i + 1; + } + else if ( p[i] == '<' ) + ++level; + else if ( p[i] == '>' ) + --level; + } + } + + m_signature = m_name; + m_signature += "("; + + QListIterator<PCOPType> it( m_params ); + for( ; it.current(); ++it ) + { + if ( !it.atFirst() ) + m_signature += ','; + m_signature += it.current()->signature(); + } + + m_signature += ")"; + + } + + PCOPMethod::~PCOPMethod() + { + delete m_type; + if (m_py_method) { + Py_DECREF(m_py_method); + } + } + + bool PCOPMethod::setPythonMethod(PyObject *method) { + if (method && PyMethod_Check(method)) { + + if (m_py_method) { + Py_DECREF(m_py_method); + } + + m_py_method = method; + Py_INCREF(m_py_method); + + return true; + } + return false; + } + + int PCOPMethod::paramCount() const + { + return m_params.count(); + } + + PCOPType* PCOPMethod::param( int i ) + { + return m_params.at( i ); + } + + const PCOPType* PCOPMethod::param( int i ) const + { + return ((PCOPMethod*)this)->m_params.at( i ); + } + + PCOPClass::PCOPClass( const QCStringList& methods ) + { + m_methods.setAutoDelete( true ); + + QCStringList::ConstIterator it = methods.begin(); + for( ; it != methods.end(); ++it ) + { + PCOPMethod* m = new PCOPMethod( *it ); + m_methods.insert( m->m_name, m ); + } + } + + PCOPClass::~PCOPClass() + { + } + + const PCOPMethod* PCOPClass::method( const QCString &name, PyObject *argTuple ) + { + if ( !argTuple ) + return m_methods[ name ]; + + QAsciiDictIterator<PCOPMethod> it( m_methods ); + for (; it.current(); ++it ) + if ( it.currentKey() == name && + it.current()->paramCount() == PyTuple_Size( argTuple ) ) + { + // ok, name and argument count match, now check if the python + // can be marshalled to the qt/dcop type + + PCOPMethod *m = it.current(); + + bool fullMatch = true; + + for ( int i = 0; i < m->paramCount(); ++i ) + if ( !m->param( i )->isMarshallable( PyTuple_GetItem( argTuple, i ) ) ) + { + fullMatch = false; + break; + } + + if ( fullMatch ) + return m; + } + + return 0; + } + + + // Client + + Client::Client() : m_dcop(NULL), m_qapp(NULL) + { + ImportedModules::setInstance( new ImportedModules ); + int argc = 0; + char **argv = NULL; + m_qapp = new QApplication(argc,argv,false); + } + + Client::~Client() + { +// if (m_qapp) delete m_qapp; + if (m_dcop) delete m_dcop; + } + + void Client::processEvents() { + if (m_qapp) { +// kdDebug(70001) << "Processing events..." << endl; + m_qapp->processEvents(); + } + } + + DCOPClient *Client::dcop() { + if ( !m_dcop ) { + m_dcop = new DCOPClient; + if ( !m_dcop->attach() ) + kdWarning(70001) << "Could not attach to DCOP server"; + } + return m_dcop; + } + + Client *Client::instance() { return s_instance; } + Client *Client::s_instance = new Client; + + +//////////////////////////////////////////////// +// +// Methods accessed by python +// +//////////////////////////////////////////////// + + PyObject* dcop_call( PyObject* /*self*/, PyObject* args ) + { + char *arg1; + char *arg2; + char *arg3; + PyObject* tuple; + + if ( !PyArg_ParseTuple( args, (char*)"sssO", &arg1, &arg2, &arg3, &tuple ) ) + return NULL; + + if ( !PyTuple_Check( tuple ) ) + return NULL; + + QByteArray replyData; + QCString replyType; + QByteArray data; + QDataStream params( data, IO_WriteOnly ); + + QCString appname( arg1 ); + QCString objname( arg2 ); + QCString funcname( arg3 ); + + // + // Remove escape characters + // + if ( objname[0] == '_' ) + objname = objname.mid( 1 ); + if ( funcname[0] == '_' ) + funcname = funcname.mid( 1 ); + + DCOPClient* dcop = Client::instance()->dcop(); + + // + // Determine which functions are available. + // + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( appname, objname, &ok ); + if ( !ok ) + { + PyErr_SetString( PyExc_RuntimeError, "Object is not accessible." ); + return NULL; + } + + // for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) { + // qDebug( "%s", (*it).data() ); + // } + + // + // Create a parse tree and search for the method + // + // ### Check wether that is sane + PCOPClass c( funcs ); + + // qDebug("Parsing done."); + + // Does the requested method exist ? + const PCOPMethod* m = c.method( funcname, tuple ); + if ( !m ) + { + PyErr_SetString( PyExc_RuntimeError, "DCOP: Unknown method." ); + return NULL; + } + + QCString signature = m->signature(); + kdDebug(70001) << "The signature is " << signature.data() << endl; + + kdDebug(70001) << "The method takes " << m->paramCount() << " parameters" << endl; + + // + // Marshal the parameters. + // + + int param_count = m->paramCount(); + for( int p = 0; p < param_count; ++p ) + { + PyObject* o = PyTuple_GetItem( tuple, p ); + // #### Check for errors + if ( !m->param( p )->marshal( o, params ) ) + { + kdDebug(70001) << "QD: Could not marshal paramater %i" << p << endl; + PyErr_SetString( PyExc_RuntimeError, "DCOP: marshaling failed" ); + return NULL; + } + } + + kdDebug(70001) << "Calling " << appname.data() << " " << objname.data() << " " << signature.data() << endl; + +// ASSERT( Client::instance()->dcop() != 0 ); + ASSERT(dcop); + + if ( !dcop->call( appname, objname, signature, data, replyType, replyData ) ) + { + PyErr_SetString( PyExc_RuntimeError, "DCOP: call failed" ); + return NULL; + } + + kdDebug(70001) << "The return type is " << replyType.data() << endl; + + // + // Now decode the return type. + // + // ### Check wether that was sane + PCOPType type( replyType ); + QDataStream reply(replyData, IO_ReadOnly); + return type.demarshal( reply ); + + } + + PyObject* application_list( PyObject */*self*/, PyObject */*args*/ ) + { + QCStringList apps = Client::instance()->dcop()->registeredApplications(); + + PyObject *l = PyList_New( apps.count() ); + + QCStringList::ConstIterator it = apps.begin(); + QCStringList::ConstIterator end = apps.end(); + unsigned int i = 0; + for (; it != end; ++it, i++ ) + PyList_SetItem( l, i, PyString_FromString( (*it).data() ) ); + + return l; + } + + PyObject *object_list( PyObject */*self*/, PyObject *args) { + const char *app; + if (PyArg_ParseTuple(args, (char*)"s", &app)) { + QCStringList objects = Client::instance()->dcop()->remoteObjects(QCString(app)); + return make_py_list(objects); + } + return NULL; + } + + PyObject *method_list( PyObject */*self*/, PyObject *args) { + const char *app, *obj; + if (PyArg_ParseTuple(args, (char*)"ss", &app, &obj)) { + QCStringList methods = Client::instance()->dcop()->remoteFunctions(QCString(app), QCString(obj) ); + return make_py_list(methods); + } + return NULL; + } + + PyObject *register_as( PyObject */*self*/, PyObject *args) { + const char *appid; + int add_pid = 1; + if (PyArg_ParseTuple(args, (char*)"s|i", &appid, &add_pid)) { + QCString actual_appid = Client::instance()->dcop()->registerAs(QCString(appid), add_pid!=0); + return PyString_FromString(actual_appid.data()); + } + return NULL; + } + + PyObject *create_dcop_object( PyObject */*self*/, PyObject *args) { + PyObject *py_dcop_object; + const char *objid = NULL; + if (PyArg_ParseTuple(args, (char*)"O|s", &py_dcop_object, &objid)) { + Py_INCREF(py_dcop_object); + PCOPObject *obj = objid ? new PCOPObject(py_dcop_object, objid) : new PCOPObject(py_dcop_object); + return PyCObject_FromVoidPtr( (void*)obj, delete_dcop_object ); + } + return NULL; + } + + /** + * pcop.set_method_list( <dcopobject cobject>, <method list> ) + * where <method list> is a list of tuples + * [ ('method signature', python method), ... ] + */ + PyObject *set_method_list( PyObject */*self*/, PyObject *args) { + PyObject *c_obj; + PyObject *method_list; + if (PyArg_ParseTuple(args, (char*)"OO", &c_obj, &method_list) && + PyCObject_Check(c_obj) && + PyList_Check(method_list)) { + + // extract each tuple from the list, aborting if any is invalid + QAsciiDict<PyObject> meth_list; + int size = PyList_Size(method_list); + for(int c=0;c<size;c++) { + PyObject *tuple = PyList_GetItem(method_list,c); + const char *method_signature = NULL; + PyObject *py_method = NULL; + if (!PyArg_ParseTuple(tuple, (char*)"sO", &method_signature, &py_method)) + return NULL; + Py_INCREF(py_method); + meth_list.insert(method_signature, py_method); + } + + PCOPObject *obj = (PCOPObject*)PyCObject_AsVoidPtr(c_obj); + if (obj) { + if (!obj->setMethodList(meth_list)) return NULL; + } + Py_INCREF(Py_None); + return Py_None; + } + return NULL; + } + + PyObject *get_method_list(PyObject */*self*/, PyObject *args) { + PyObject *c_obj; + if (PyArg_ParseTuple(args, (char*)"O", &c_obj)) { + if (PyCObject_Check(c_obj)) { + PCOPObject *obj = (PCOPObject*)PyCObject_AsVoidPtr(c_obj); + return obj->methodList(); + } + } + return NULL; + } + + PyObject *connect_DCOP_Signal( PyObject */*self*/, PyObject *args) { + const char *sender; + const char *senderObj; + const char *signal; + const char *receiverObj; + const char *slot; + + int volint = 0; + if (PyArg_ParseTuple(args, (char*)"sssss|i", &sender, &senderObj, &signal, &receiverObj, &slot, &volint)) { + bool success = Client::instance()->dcop()->connectDCOPSignal(QCString(sender), QCString(senderObj), QCString(signal), QCString(receiverObj), QCString(slot), (volint == 1)?true:false); + return Py_BuildValue("i", success?1:0); + } + return NULL; + } + + PyObject *disconnect_DCOP_Signal( PyObject *self, PyObject *args) { + const char *sender; + const char *senderObj; + const char *signal; + const char *receiverObj; + const char *slot; + + if (PyArg_ParseTuple(args, (char*)"sssss", &sender, &senderObj, &signal, &receiverObj, &slot)) { + bool success = Client::instance()->dcop()->disconnectDCOPSignal(QCString(sender), QCString(senderObj), QCString(signal), QCString(receiverObj), QCString(slot)); + return Py_BuildValue("i", success?1:0); + } + return NULL; + + } + + + + void delete_dcop_object(void *vp) { + if (vp) { + PCOPObject *obj = (PCOPObject*)vp; + delete obj; + } + } + + PyObject *process_events( PyObject */*self*/, PyObject */*args*/) { + Client::instance()->processEvents(); + Py_INCREF(Py_None); + return Py_None; + } + + // helpers + + PyObject *make_py_list( const QCStringList &qt_list) { + PyObject *l = PyList_New(qt_list.count()); + uint c=0; + for(QCStringList::ConstIterator it = qt_list.begin(); + it!=qt_list.end(); + ++it,c++) + PyList_SetItem(l, c, PyString_FromString( (*it).data() ) ); + return l; + } + +} + + +PyMethodDef PCOPMethods[] = { + { (char*)"dcop_call", PythonDCOP::dcop_call, METH_VARARGS, (char*)"Make a call to DCOP." }, + { (char*)"app_list", PythonDCOP::application_list, METH_VARARGS, (char*)"Return a list of DCOP registered application." }, + { (char*)"obj_list", PythonDCOP::object_list, METH_VARARGS, (char*)"Return a list of objects for a DCOP registered application."}, + { (char*)"method_list", PythonDCOP::method_list, METH_VARARGS, (char*)"Return a list of methods for a DCOP object."}, + { (char*)"register_as", PythonDCOP::register_as, METH_VARARGS, (char*)"Register the application with DCOP."}, + { (char*)"create_dcop_object", PythonDCOP::create_dcop_object, METH_VARARGS, (char*)"Creates a DCOP Object instance."}, + { (char*)"process_events", PythonDCOP::process_events, METH_VARARGS, (char*)"Processes QT events."}, + { (char*)"set_method_list", PythonDCOP::set_method_list, METH_VARARGS, (char*)"Set the list of methods for a DCOP server object."}, + { (char*)"connect_dcop_signal", PythonDCOP::connect_DCOP_Signal, METH_VARARGS, (char*)"Connect a dcop signal."}, + { (char*)"disconnect_dcop_signal", PythonDCOP::disconnect_DCOP_Signal, METH_VARARGS, (char*)"Disconnect a dcop signal."}, + { NULL, NULL, 0, NULL } /* Sentinel */ +}; + +extern "C" +{ + + void initpcop() + { + (void) Py_InitModule( (char*)"pcop", PCOPMethods ); + } + +} + + diff --git a/dcoppython/shell/pcop.h b/dcoppython/shell/pcop.h new file mode 100644 index 00000000..ecfe0f65 --- /dev/null +++ b/dcoppython/shell/pcop.h @@ -0,0 +1,202 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey (linux@jrockey.com) * + * Original code by Torben Weis * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef __pcop_h__ +#define __pcop_h__ + +#include <Python.h> + +#include <qcstring.h> +#include <qlist.h> +#include <qasciidict.h> + +#include <dcopclient.h> +#include <dcopobject.h> + +class QDataStream; + +namespace PythonDCOP { + class Client; + class PCOPMethod; + class ImportedModules; + + // Python interface + PyObject *dcop_call( PyObject* self, PyObject* args ); + PyObject *application_list( PyObject *self, PyObject *args ); + PyObject *object_list(PyObject *self, PyObject *args ); + PyObject *method_list(PyObject *self, PyObject *args ); + PyObject *register_as( PyObject *self, PyObject *args); + PyObject *create_dcop_object( PyObject *self, PyObject *args); + PyObject *set_method_list( PyObject *self, PyObject *args); + PyObject *connect_DCOP_Signal( PyObject *self, PyObject *args); + PyObject *disconnect_DCOP_Signal( PyObject *self, PyObject *args); + + + // helpers... + void delete_dcop_object(void *vp); + PyObject *make_py_list(const QCStringList &qt_list); + + /** + * Used by the Python interface to talk to DCOP + */ + class Client { + public: + Client(); + ~Client(); + void processEvents(); + DCOPClient *dcop(); +// ImportedModules *module() const { return m_module; } + static Client *instance(); + protected: + DCOPClient *m_dcop; +// ImportedModules *m_module; + static Client *s_instance; + QApplication *m_qapp; + }; + + /** + * Class representing a DCOPObject. + * This class represents a DCOP object in a "server" capacity. + */ + class PCOPObject : public DCOPObject + { + public: + /** + * Construct from a pointer to the Python object holding this CObject. + */ + PCOPObject(PyObject *py_obj); + + /** + * Construct from a pointer to the Python object holding this CObject and + * a DCOP object ID. + */ + PCOPObject(PyObject *py_obj, const char *objid); + + virtual ~PCOPObject(); + + /** + * Process method fun, whose arguments are marshalled in data. + * Set replyType to be the reply type and marshall the reply data into replyData. + */ + virtual bool process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData); + + /** + * Return list of supported functions (methods). + */ + virtual QCStringList functions(); + + /** + * Set the list of methods that this object handles. + * The key of the QT dictionary is the method signature; the data in + * the dictionary is a pointer to the python method to which it corresponds. + */ + virtual bool setMethodList(QAsciiDict<PyObject> meth_list); + + /** + * Returns the current list of methods, as set by setMethodList. + */ + virtual PyObject *methodList(); + + /** + * Matches an 'incoming' method signature (fun) and returns a PCOPMethod pointer, + * or NULL if none match. + */ + PCOPMethod *matchMethod(const QCString &fun); + + private: + virtual bool py_process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData); + + /** + * The Python object holding this CObject. + */ + PyObject *m_py_obj; + + /** + * The list of methods this object supports. + */ + QAsciiDict<PCOPMethod> m_methods; + + }; + + /** + * Class representing a data type, with methods for DCOP marshalling and unmarshalling. + */ + class PCOPType + { + public: + PCOPType( const QCString& dcop_representation); + ~PCOPType(); + + QCString signature() const; + + PyObject* demarshal( QDataStream& str ) const; + bool marshal( PyObject* obj, QDataStream& str ) const; + + // checks if the given PyObject can be marshalled as this PCOPType + bool isMarshallable( PyObject *obj ) const; + + const QCString &type() const { return m_type; } + const PCOPType *leftType() const { return m_leftType; } + const PCOPType *rightType() const { return m_rightType; } + + // TODO: make these private + QCString m_type; + PCOPType* m_leftType; + PCOPType* m_rightType; + + }; + + /** + * Class representing a DCOP method + */ + class PCOPMethod + { + public: + PCOPMethod( const QCString& dcop_signature ); + ~PCOPMethod(); + + int paramCount() const; +// QCString signature() const; +// QCString name() const; + PCOPType* param( int ); + const PCOPType* param( int ) const; + + bool setPythonMethod(PyObject *py_method); + PyObject *pythonMethod() const { return m_py_method; } + const QCString &signature() const { return m_signature; } + const QCString &name() const { return m_name; } + const PCOPType *type() const { return m_type; } + + QCString m_signature; + QCString m_name; + PCOPType* m_type; + QList<PCOPType> m_params; + private: + PyObject *m_py_method; + }; + + /** + * Class representing a DCOP class. + */ + class PCOPClass + { + public: + PCOPClass( const QCStringList& dcop_style_methods); + ~PCOPClass(); + + const PCOPMethod* method( const QCString &name, PyObject *argTuple = 0 ); + + QCStringList m_ifaces; + QAsciiDict<PCOPMethod> m_methods; + }; + +} + +#endif diff --git a/dcoppython/test/Makefile.am b/dcoppython/test/Makefile.am new file mode 100644 index 00000000..35deee5d --- /dev/null +++ b/dcoppython/test/Makefile.am @@ -0,0 +1,3 @@ + +subdirs = dcopserver + diff --git a/dcoppython/test/README-server b/dcoppython/test/README-server new file mode 100644 index 00000000..3d2528b8 --- /dev/null +++ b/dcoppython/test/README-server @@ -0,0 +1,19 @@ +The file server.py contains an example of using the DCOP bindings to run a DCOP server in Python. + +Run python server.py, then in another console: + +[julian] julian$ dcop `dcop | grep petshop` +qt +parrot +[julian] julian$ dcop `dcop | grep petshop` parrot +QCStringList interfaces() +QCStringList functions() +QString squawk(QString) +void setAge(int) +int age() +[julian] julian$ dcop `dcop | grep petshop` parrot setAge 5 +[julian] julian$ dcop petshop-29530 parrot squawk 'How many volts for a vrooom?' +This parrot, 5 months old, squawks: How many volts for a vrooom? +[julian] julian$ + + diff --git a/dcoppython/test/automate_presentation.py b/dcoppython/test/automate_presentation.py new file mode 100755 index 00000000..0c75e108 --- /dev/null +++ b/dcoppython/test/automate_presentation.py @@ -0,0 +1,30 @@ +# Python version of David Faure's <faure@kde.org> dcop presentation automation script for kpresenter +# +# Simon Hausmann <hausmann@kde.org> +from time import sleep +import pcop +import pydcop + +app = pydcop.anyAppCalled( "kpresenter" ) + +if not app: raise RuntimeError, "Couldn't find a running KPresenter" + +doc = app.KoApplicationIface.getDocuments()[0] +view = doc.view(0) + +startAction = view.action( "screen_start" ) + +print "Starting Presentation %s" % doc.url() + +startAction.activate() + +sleep( 5 ) + +act = view.action( "screen_next" ) +while startAction.enabled() == 0: + sleep( 10 ) + if startAction.enabled() == 0: + act.activate() + +view.screenStop() +print "Presentation finished." diff --git a/dcoppython/test/dcopserver/Makefile.am b/dcoppython/test/dcopserver/Makefile.am new file mode 100644 index 00000000..61610ae5 --- /dev/null +++ b/dcoppython/test/dcopserver/Makefile.am @@ -0,0 +1,36 @@ +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kdedcoptest.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kdedcoptest.pot + +KDE_ICON = kdedcoptest + +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kdedcoptest + +# the application source, library search path, and link libraries +kdedcoptest_SOURCES = main.cpp kdedcoptest.cpp kdedcoptest_iface.skel mainclass.cpp +kdedcoptest_LDFLAGS = $(KDE_RPATH) $(all_libraries) +kdedcoptest_LDADD = $(LIB_KDEUI) + +# this is where the desktop file will go +shelldesktopdir = $(kde_appsdir)/Utilities +shelldesktop_DATA = kdedcoptest.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/kdedcoptest +shellrc_DATA = kdedcoptestui.rc + +h_inc.h: gen.py + python gen.py diff --git a/dcoppython/test/dcopserver/README b/dcoppython/test/dcopserver/README new file mode 100644 index 00000000..47e69273 --- /dev/null +++ b/dcoppython/test/dcopserver/README @@ -0,0 +1,8 @@ +What's this doing here? It's here to help if you're developing the Python DCOP bindings themselves. You don't need it if you just want to _use_ the bindings. + +It's a simple DCOP server in C++, that can be used to conveniently test the Python DCOP interface (or any other DCOP binding, for that matter). The script gen.py generates simple get/set DCOP methods for a variety of types. + +Note that gen.py is _not_ automatically invoked from the Makefile, so you should run it yourself before doing make. + +Julian Rockey +kde@jrockey.com diff --git a/dcoppython/test/dcopserver/gen.py b/dcoppython/test/dcopserver/gen.py new file mode 100644 index 00000000..ae2947af --- /dev/null +++ b/dcoppython/test/dcopserver/gen.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +type_list = ['KURL', 'QDate', 'QTime', 'QDateTime', 'QRect', 'QString', 'int', 'QFont', 'QSize', 'QPoint', 'QPointArray' ] + +inc_exceptions = {'QDate': None, 'QTime': None, 'KURL' : 'kurl'} + +iface_inc_list = ['dcopobject'] + +iface_inc_list += [ t.lower() for t in type_list if t[0]=='Q' and t not in inc_exceptions ] +iface_inc_list += inc_exceptions.values() + +iface_inc_1 = ['class DCOPDemoIface : virtual public DCOPObject {', + ' K_DCOP', + ' k_dcop:'] +h_inc = [] +cpp_inc = [] + +for t in type_list: + iface_inc_1.append("virtual void set%sVal(const %s &val) = 0; " % (t,t) ) + iface_inc_1.append("virtual %s %sVal() const = 0;" % (t,t) ) + + h_inc.append("virtual void set%sVal(const %s &val); " % (t,t) ) + h_inc.append("virtual %s %sVal() const;" % (t,t) ) + h_inc.append("%s m_%sValue;" % (t,t) ) + + cpp_inc.append("void MainClass::set%sVal(const %s & val) {" % (t,t) ) + cpp_inc.append(" m_%sValue = val; }" % t) + cpp_inc.append("%s MainClass::%sVal() const {" % (t,t) ) + cpp_inc.append(" return m_%sValue; }" % t) + +iface_inc = [] +for inc in iface_inc_list: + if inc: iface_inc.append("#include <%s.h>" % inc) +iface_inc += iface_inc_1 +iface_inc.append("};") + +files = {'kdedcoptest_iface.h': iface_inc, + 'h_inc.h': h_inc, + 'cpp_inc.h': cpp_inc + } + +for (fname,data) in files.items(): + outf = file(fname,'w') + for d in data: + outf.write(d+'\n') + outf.close() diff --git a/dcoppython/test/dcopserver/hi16-app-kdedcoptest.png b/dcoppython/test/dcopserver/hi16-app-kdedcoptest.png Binary files differnew file mode 100644 index 00000000..4ed606c1 --- /dev/null +++ b/dcoppython/test/dcopserver/hi16-app-kdedcoptest.png diff --git a/dcoppython/test/dcopserver/hi32-app-kdedcoptest.png b/dcoppython/test/dcopserver/hi32-app-kdedcoptest.png Binary files differnew file mode 100644 index 00000000..45ae1a11 --- /dev/null +++ b/dcoppython/test/dcopserver/hi32-app-kdedcoptest.png diff --git a/dcoppython/test/dcopserver/kdedcoptest.cpp b/dcoppython/test/dcopserver/kdedcoptest.cpp new file mode 100644 index 00000000..3046eaac --- /dev/null +++ b/dcoppython/test/dcopserver/kdedcoptest.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003 Julian Rockey <kde@jrockey.com> + */ + +#include "kdedcoptest.h" + +#include <qlabel.h> + +#include <kmainwindow.h> +#include <klocale.h> + +KDEDcopTest::KDEDcopTest() + : KMainWindow( 0, "KDEDcopTest" ) +{ + // set the shell's ui resource file + //setXMLFile("kdedcoptestui.rc"); + + //new QLabel( "Hello World", this, "hello label" ); + m_mainClass = new MainClass(); +} + +KDEDcopTest::~KDEDcopTest() +{ + if (m_mainClass) delete m_mainClass; +} + +#include "kdedcoptest.moc" diff --git a/dcoppython/test/dcopserver/kdedcoptest.desktop b/dcoppython/test/dcopserver/kdedcoptest.desktop new file mode 100644 index 00000000..beee065d --- /dev/null +++ b/dcoppython/test/dcopserver/kdedcoptest.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=KDEDcopTest +Exec=kdedcoptest +Icon=kdedcoptest +Type=Application +Comment=A simple KDE Application diff --git a/dcoppython/test/dcopserver/kdedcoptest.h b/dcoppython/test/dcopserver/kdedcoptest.h new file mode 100644 index 00000000..42a54da6 --- /dev/null +++ b/dcoppython/test/dcopserver/kdedcoptest.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003 Julian Rockey <kde@jrockey.com> + */ + +#ifndef _KDEDCOPTEST_H_ +#define _KDEDCOPTEST_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kmainwindow.h> + +#include "mainclass.h" + +/** + * @short Application Main Window + * @author Julian Rockey <kde@jrockey.com> + * @version 0.1 + */ +class KDEDcopTest : public KMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KDEDcopTest(); + + /** + * Default Destructor + */ + virtual ~KDEDcopTest(); +private: + MainClass *m_mainClass; +}; + +#endif // _KDEDCOPTEST_H_ diff --git a/dcoppython/test/dcopserver/kdedcoptestui.rc b/dcoppython/test/dcopserver/kdedcoptestui.rc new file mode 100644 index 00000000..7f6ee828 --- /dev/null +++ b/dcoppython/test/dcopserver/kdedcoptestui.rc @@ -0,0 +1,8 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kdedcoptest" version="1"> +<MenuBar> + <Menu name="custom"><text>C&ustom</text> + <Action name="custom_action" /> + </Menu> +</MenuBar> +</kpartgui> diff --git a/dcoppython/test/dcopserver/main.cpp b/dcoppython/test/dcopserver/main.cpp new file mode 100644 index 00000000..5335ffe9 --- /dev/null +++ b/dcoppython/test/dcopserver/main.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003 Julian Rockey <kde@jrockey.com> + */ + +#include "kdedcoptest.h" +#include <kapplication.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> + +static const char *description = + I18N_NOOP("A KDE KPart Application"); + +static const char *version = "0.1"; + +static KCmdLineOptions options[] = +{ +// { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + { 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + KAboutData about("kdedcoptest", I18N_NOOP("KDEDcopTest"), version, description, + KAboutData::License_GPL, "(C) 2003 Julian Rockey", 0, 0, "kde@jrockey.com"); + about.addAuthor( "Julian Rockey", 0, "kde@jrockey.com" ); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + KDEDcopTest *mainWin = 0; + + if (app.isRestored()) + { + RESTORE(KDEDcopTest); + } + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + // TODO: do something with the command line args here + + mainWin = new KDEDcopTest(); + app.setMainWidget( mainWin ); + mainWin->show(); + + args->clear(); + } + + int ret = app.exec(); + + delete mainWin; + return ret; +} diff --git a/dcoppython/test/dcopserver/mainclass.cpp b/dcoppython/test/dcopserver/mainclass.cpp new file mode 100644 index 00000000..d74b7f10 --- /dev/null +++ b/dcoppython/test/dcopserver/mainclass.cpp @@ -0,0 +1,20 @@ +/*************************************************************************** + * Copyright (C) 2003 by Julian Rockey * + * kde@jrockey.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#include "mainclass.h" + + +MainClass::MainClass() + : DCOPDemoIface(), DCOPObject("test") +{} + +MainClass::~MainClass() +{} + +#include "cpp_inc.h" diff --git a/dcoppython/test/dcopserver/mainclass.h b/dcoppython/test/dcopserver/mainclass.h new file mode 100644 index 00000000..7d4f7f4e --- /dev/null +++ b/dcoppython/test/dcopserver/mainclass.h @@ -0,0 +1,23 @@ + +#ifndef MAINCLASS_H +#define MAINCLASS_H + + +#include "kdedcoptest_iface.h" + +/** + * + * Julian Rockey + **/ +class MainClass : virtual public DCOPDemoIface +{ +public: + MainClass(); + + ~MainClass(); + +#include "h_inc.h" + +}; + +#endif diff --git a/dcoppython/test/server.py b/dcoppython/test/server.py new file mode 100644 index 00000000..479b0236 --- /dev/null +++ b/dcoppython/test/server.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# This is an example of a DCOP serving application written in Python, using +# the dcoppython KDE bindings. + +# something goes wrong if you don't import pcop first. +# Have to do this till I find out why... +import pcop +import pydcop + +class ParrotObject(pydcop.DCOPServerObject): + """DCOP server object""" + + def __init__(self, id='parrot'): + pydcop.DCOPServerObject.__init__(self, id) + + # DCOP needs types, so we need to initialise the object with the methods that + # it's going to provide. + self.setMethods( [ + ('int age()', self.get_age), + ('void setAge(int)', self.set_age), + ('QString squawk(QString)', self.squawk), + ]) + + # set up object variables + self.parrot_age = 7 + + def get_age(self): + return self.parrot_age + + def set_age(self,age): + self.parrot_age = age + + def squawk(self, what_to_squawk): + return "This parrot, %i months old, squawks: %s" % (self.parrot_age, what_to_squawk) + + +appid = pydcop.registerAs('petshop') +print "Server: %s starting" % appid + +parrot = ParrotObject() +another_parrot = ParrotObject('polly') + +# Enter event loop +while 1: + pydcop.processEvents() + + diff --git a/dcoppython/test/signal.py b/dcoppython/test/signal.py new file mode 100644 index 00000000..f9d087c6 --- /dev/null +++ b/dcoppython/test/signal.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# This is an example of how to use DCOP Signals with +# the dcoppython KDE bindings. + +# something goes wrong if you don't import pcop first. +# Have to do this till I find out why... +import pcop +import pydcop + +class MyObject(pydcop.DCOPServerObject): + """DCOP server object""" + + def __init__(self, id='pythontest'): + pydcop.DCOPServerObject.__init__(self, id) + + # DCOP needs types, so we need to initialise the object with the methods that + # it's going to provide. + self.setMethods( [('void test(QString)', self.test)]) + + def test(self, data): + print "New Weather for " + data + +appid = pydcop.registerAs('dcopSignalTest') +print "Server: %s starting" % appid + +pytest = MyObject() + +sender = "KWeatherService" +senderObj = "WeatherService" +signal = "fileUpdate(QString)" +receiverObj = "pythontest" +slot = "test(QString)" +volatile = 1 + +pydcop.connectDCOPSignal(sender,senderObj,signal,receiverObj,slot,volatile) +# Enter event loop +while 1: + pydcop.processEvents() + + diff --git a/dcoppython/test/test.py b/dcoppython/test/test.py new file mode 100644 index 00000000..5ead172a --- /dev/null +++ b/dcoppython/test/test.py @@ -0,0 +1,15 @@ +import pcop +import pydcop + +#res = pcop.dcop_call( "kspread", "default", "getDocuments", () ) + +res = pydcop.anyAppCalled("kspread").default.getDocuments() +print res +print res[0].appname +print res[0].name + +m = res[0].map() + +print m.tableNames() + +print "done" diff --git a/dcoppython/test/test2.py b/dcoppython/test/test2.py new file mode 100644 index 00000000..1a56f917 --- /dev/null +++ b/dcoppython/test/test2.py @@ -0,0 +1,28 @@ + +import pcop +import pydcop + +app = pydcop.anyAppCalled( "kspread" ); + +res = app.default.getDocuments() + +print res +print res[0].appname +print res[0].name + +m = res[0].map() + +print m.tableNames() + +x = m.table('Sheet2') + +if x: + print x + print "====================" + print x._name + print "====================" + print x._name() + print "====================" +else: print "Could not find sheet called Sheet2" + +print "done" diff --git a/dcoppython/test/test3.py b/dcoppython/test/test3.py new file mode 100644 index 00000000..aa13ed8b --- /dev/null +++ b/dcoppython/test/test3.py @@ -0,0 +1,14 @@ + +import pcop +import pydcop + +app = pydcop.anyAppCalled("kspread") + +table = app.default.getViews()[0] + +table.setSelection( ( 2, 2, 4, 6 ) ) + +table.setSelectionBgColor( (100, 100, 240) ) +print rect + +print "done" diff --git a/dcoppython/test/test4.py b/dcoppython/test/test4.py new file mode 100644 index 00000000..b87a071f --- /dev/null +++ b/dcoppython/test/test4.py @@ -0,0 +1,24 @@ + +import pcop +import pydcop + +app = pydcop.anyAppCalled("kspread") + +doc = app.default.getDocuments()[0] + +table = doc.map().tables()[0] + +table.cell( 1, 1 ).setValue( 1 ) + +x = 2 + +while x < 5: + y = 2 + while y < 5: + previousCell = table.cell( x - 1, y - 1 ) + cell = table.cell( x, y ) + cell.setValue( previousCell.value() + 1.0 ) + y = y + 1 + x = x + 1 + + |