diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-08-16 09:06:37 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-08-16 09:06:37 +0000 |
commit | 39d98386f72c65826e162e3e8fd36752ec469252 (patch) | |
tree | 5cec746207c4c892d064beafca1de94568a3aeb9 /doc/dcopext.html | |
download | pytde-39d98386f72c65826e162e3e8fd36752ec469252.tar.gz pytde-39d98386f72c65826e162e3e8fd36752ec469252.zip |
Move python-kde3 to the more correct python-trinity
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/libraries/python-trinity@1247483 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'doc/dcopext.html')
-rw-r--r-- | doc/dcopext.html | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/doc/dcopext.html b/doc/dcopext.html new file mode 100644 index 0000000..b9eef07 --- /dev/null +++ b/doc/dcopext.html @@ -0,0 +1,378 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN + "http://www.w3.org/TR/html4/loose.dtd""> +<html>p +<head> + <title>Examples</title> + <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <meta name="GENERATOR" content="Quanta Plus"> +</head> +<p> +<DIV +CLASS="NAVHEADER" +><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0"> +<TR><TH COLSPAN="3" ALIGN="center">Python Bindings for KDE (PyKDE-3.16.0)</TH></TR> +<TR><TD WIDTH="10%" ALIGN="left" VALIGN="bottom"><A HREF="examples.html" ACCESSKEY="P">Prev</A></TD> +<TD WIDTH="80%" ALIGN="center" VALIGN="bottom"></TD> +<TD WIDTH="10%" ALIGN="right" VALIGN="bottom"><A HREF="limits.html" ACCESSKEY="N">Next</A></TD> +</TR> +</TABLE><HR ALIGN="LEFT" WIDTH="100%"></DIV> + +<h1>DCOP and Extensions</h1> +<p> +DCOP is KDE's acronym for it's "Desktop Communications-Oriented Protocol" - basically a +lightweight and simple mechanism for inter-process communications (IPC). DCOP allows two +running applications to exchange messages or other information or exercise control over +each other. +</p> +<p> +While the DCOP implementation is convenient for C++ programmers, it presents some difficulties +for Python programmers. The DCOP extensions that have been added to PyKDE should make most +DCOP applications (either DCOP-client or DCOP-enabled applications) simple to write and +reliable to run +</p> +<h2>What Extensions?</h2> +There are three basic extensions added to PyKDE that are not part of KDE itself: +<dl> +<dt>Packing/Unpacking QByteArrays</dt> +<dd> +DCOP passes data between applications using QByteArrays. QByteArrays can be difficult to +pack or unpack using PyQt or PyKDE, so PyKDE has additional methods (dcop_add and dcop_next) +to make these operations simpler in Python +</dd> +<dt>Client Extensions</dt> +<dd> +PyKDE's DCOP client extensions make it easy and natural to call DCOP methods in other +DCOP-enabled applications - the application or DCOP object being referenced look like +Python classes, and the method being called looks to the programmer like a Python method. +</dd> +<dt>DCOP Enabling (Export) Extensions</dt> +<dd> +Another set of extensions makes it trivial to expose an application's methods via DCOP to +other applications. All that is required is to subclass a pre-written Python class and +provide a list of the methods to expose, along with a method signature listing the name +of the method, it's return type, and the the types of its arguments. +</dd> +</dl> +<p> +The methods for packing/unpacking QByteArrays are available to the programmer, but are +primarily used transparently by the other PyKDE DCOP extensions. The client and export extensions +are two Python modules that are included and installed as part of PyKDE. +</p> + +<h2>Calling DCOP Methods</h2> +<p> +Accessing a DCOP method in another application requires 3 pieces of information: the name of +the application to be accessed, the name of the DCOP object which holds the method to be +called, and the name of the method itself. +</p> + +<h3>Collection the Information</h3> +<p> +The easiest way to collect the required information is to use the kdcop application that +comes with PyKDE. kdcop is graphical application that looks like the image shown. +</p> +<IMG src="images/kdcop1.png" align="middle" border="0"> +<h3>Application/Object/Method Information</h3> +<p> +Look at the entry for kicker, which has been expanded in the image. Underneath kicker (the +application name - kicker is the panel on the standard KDE screen) is a list of DCOP objects, +for example, Panel. Under each object is a list of methods the application/object exposes, for +example, "int panelPosition ()". This indicates the method panelPosition takes no arguments +and returns an integer. +</p> +<h3>Writing the Code</h3> +<p> +There are two ways to use the DCOP extensions to call the panelPosition method. The first is +from the application level, the second is from the object level. We can use the "application +level" in this case, because the object name "Panel" can be valid Python identifier (not all +object names have this property). +</p> +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> +import dcopext +# ! other imports not shown ! + +app = KApplication () +dcop = app.dcopClient () + +d = dcopext.DCOPApp ("kicker", dcop) +ok, panelPos = d.Panel.panelPosition () + +</PRE></TD></TR></TABLE> +<p> +That's all there's to it in this case. We import dcopext, which contains the client extension +classes; from the KApplication instance, we "borrow" the DCOPClient instance (dcop); we create a +DCOPApp instance, passing it the name of the app ("kicker") and the DCOPClient instance; we +call kicker's Panel object's panelPosition method (d.Panel.panelPosition); lastly, the integer +value is returned to our application (panelPos) as the second item in a tuple - the first element +of the tuple (ok) is a boolean value indicating whether the call succeeded (True) or failed (False). +</p> +<p> +Many of the DCOP object names can't be used as Python identifiers (for example,"0x8239ae0" or +KIO::Scheduler in kicker, or EditInterface#1, which kwrite exports). In that case, it's +necessary to write the code at the object level, constructing a DCOPObj instead of a +DCOPApp (DCOPApp actually constructs a DCOPObj behind the scenese in the example above). +</p> +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> +import dcopext +# ! other imports not shown ! + +o = dcopext.DCOPObj ("kicker", dcop, "Panel") +ok, panelPos = o.panelPosition () + +</PRE></TD></TR></TABLE> +<p> +In this example, 'o' is a DCOPObj. In constructing 'o', we add a string representation of +the name of the object ("Panel") to the application name and DCOPClient object. We then +use the DCOPObj 'o' to call the the method (panelPosition) that the object supports. +</p> +<h3>More on Application Names</h3> +<p> +In the example above, kicker was the name of the application and the id we used to reference +the application as well. kicker is an example of a unique application - only one instance of +kicker can be running at any time. +</p> +<p> +Many applications (konqueror, for example) can have several instances running at the same +time. kdcop would display multiple instances like this: +</p> +<IMG src="images/kdcop2.png" border="0"> +<p> +kdcop shows 3 instances of konqueror running in the example above. To perform a DCOP call in +this case, we'd need to know which instance of konqueror we want to send the call to. The +suffix on each instance of konqueror is the PID of the instance running. We simply pass the +full id (app name + pid - eg konqueror-14409) when constructing DCOPApp or DCOPObj. +</p> +<p> +If you instantiate the application you want to communicate with from your own application (that +will be making the DCOP calls), methods like KApplication.startServiceByDesktopName will +let you start the app and also return both the PID of the started app and the complete +identifier string needed to initiate DCOP communications. The identifier's name portion may or +may not be the same as the name of the application (see the example_dcopexport.py example program, +whose ID is "petshop-####" (#### is the PID of the application instance). +</p> +<h3>Data Types</h3> +The DCOP extensions will support any of the following C++ data types: +<table><TR><TD>char</TD><TD>short</TD><TD>int</TD></TR> +<TR><TD>long</TD><TD>unsigned char</TD><TD>unsigned short</TD></TR> +<TR><TD>unsigned int</TD><TD>unsigned long</TD><TD>uchar</TD></TR> +<TR><TD>ushsort</TD><TD>uint</TD><TD>ulong</TD></TR> +<TR><TD>Q_INT32</TD><TD>pid_t</TD><TD>float</TD></TR> +<TR><TD>double</TD><TD>QString</TD><TD>QStringList</TD></TR> +<TR><TD>QCString</TD><TD>KURL</TD><TD>KURL::List</TD></TR> +<TR><TD>QSize</TD><TD>QRect</TD><TD>QRegion</TD></TR> +<TR><TD>QFont</TD><TD>QCursor</TD><TD>QPixmap</TD></TR> +<TR><TD>QColor</TD><TD>QColorGroup</TD><TD>QPalette</TD></TR> +<TR><TD>QBrush</TD><TD>QWidget::FocusPolicy</TD><TD>DCOPRef</TD></TR> +<TR><TD>QVariant</TD><TD>QDate</TD><TD>QTime</TD></TR> +<TR><TD>QDateTime</TD><TD>QImage</TD><TD>QKeySequence</TD></TR> +<TR><TD>QPen</TD><TD>QPicture</TD><TD>QPointArray</TD></TR> +<TR><TD>QValueList<DCOPRef></TD><TD>QValueList<QCString></TD><TD>QMap<QCString,DCOPRef></TD></TR> +<TR><TD>QMap<QCString,DCOPRef></TD><TD></TD><TD></TD></TR> +</table> +<p> +Data conversion between C++ and Python types is done transparently. The integer types +map to Python int or Python long, the decimal types to Python double. A Python string +can be used for any argument that requires a QString or QCString (return types will +always be the Qt object type). The QValueList types take or return a Python list of the +indicated object. The QMap types take or return a Python dict with the first type as +the key and the second type as data. All other types use the same object type in +Python and Qt (for instance, QPoint or QStringList). +</p> +<p> +It's possible to add support for more types in the future. To be added, a type requires +a pair of overloaded QDataStream operators ("<<" and ">>"). Types must also +exist in the libs that PyQt and PyKDE support - types specific to applications (like +konqueror) cannot be supported at this time. +</p> +<h3>Other Extension Features</h3> +<p> +The dcopext module consists of 3 classes (DCOPApp, DCOPObj and DCOPMeth) corresponding to +applications, objects and methods respectively. These classes have additional variables and methods: +<ul> +<li> DCOPApp.objects - returns a list of the applications DCOP objects. example: d.objects</li> +<li> DCOPApp.object(objname) - returns a DCOPObj for the DCOPObject. example: d.object ("Panel")</li> +<li> DCOPObj.methods - returns a list of the methods and object has. example: o.methods</li> +<li> DCOPObj.method (methname) - returns an DCOPMeth instance corresponding to the method, which +can be called. example: o.method("panelPosition")</li> +<li> DCOPMeth.valid - returns whether the method is valid or not (True/False). example: +d.Panel.panelPosition.valid</li> +<li>DCOPMeth.rtype - a method's return type. example d.Panel.panelPosition.rtype</li> +<li>DCOPMeth.argtypes - a list of the method's argument types. example d.Panel.panelPosition.argtypes</li> +<li>DCOPMeth.argnames - a list of the method's argument names. example d.Panel.panelPosition.argnames</li> +</ul> +<p> +If a method isn't valid, it's rtype, argtypes and argnames values will all be None. +</p> +</p> +<h2>DCOP Enabling a Python Application</h2> +<p> +Enabling a Python application to handle DCOP calls is even simpler than making calls as a +DCOP client. Suppose a Python application has two methods we want to appear as int getValue() +and void setValue(int). The corresponding Python methods are get_value() set_value(i). + We want to export these methods under the object "Value". Here's the code: +</p> +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> +from dcopexport import DCOPExObj +# ! other imports not shown ! + +class ValueObject (DCOPExObj): + def __init__ (self, id="Value"): + DCOPExObj.__init__ (self, id) + self.value = 0 + + self.addMethod ("int getValue()", self.get_value) + self.addMethod ("void setValue(int)", self.set_value) + + def get_value(self): + return self.value + + def set_value (self, i): + self.value = i + +</PRE></TD></TR></TABLE> +<p> +Note that the module for the DCOPExObj class is "dcopexport". The Python methods may be +part of the DCOPExObj subclass, part of another class, or global Python functions. They +must be callable from the DCOPExObj subclass being created. The dcopexport extension takes +care of everything else, including the "functions()" method which applications (yours or +kdcop, for example) can call to find out which methods are available and their return +and argument types. You can have multiple instances of DCOPExObj in a program. All of +the data types listed above are supported transparently - you don't have to pack or +unpack QByteArrays. +</p> +<h2>Packing and Unpacking QByteArrays</h2> +<p> +NOTE: It isn't necessary to use the dcop_add and dcop_next functions or worry about +QByteArrays at all when using dcopext or dcopexport as shown above. Those modules +handle the packing and unpacking details automatically behind the scenes. +</p> +<p> +The dcop_add and dcop_next functions are available in the PyKDE kdecore module (they +may be relocated to a different module in the future). They use a QDataStream to operate +on a QByteArray. The QByteArray can be thought of as a stack (a FIFO stack though) - +dcop_add pushes objects onto the stack, dcop_next pops objects off the stack. The first +object popped off will be the first object pushed on, etc. +</p> +<p> +The dcop_add function is actually a group of overloaded functions, some of which take +different argument counts. Here are some examples: +</p> +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> +from kdecore import dcop_add, dcop_next +from qt import QByteArray, QDataStream, IO_ReadOnly, IO_WriteOnly, QString,\ + QCString, QValueList<QCString> +from dcopext import numericTypes, stringTypes + +b = QByteArray () +s = QDataStream (b, IO_WriteOnly) + +i = 6 +d = 3.14 +t = QString ("Hello, World") +x = QCString ("One") +y = QCString ("Two") +z = QCString ("Three") +l = [x, y, z] + +dcop_add (s, i, "long") +dcop_add (s, d, "double") +dcop_add (s, t) +dcop_add (s, x) +dcop_add (s, l, "QValueList<QCString>") + +</PRE></TD></TR></TABLE> +<p> +Notice that for numeric types (integer or decimal) an additional string is needed to +specify the C++ type of the object - that's because Python has only 3 basic numeric +types, while C++ has at least 10 basic numeric types plus variations via typedefs. +</p> +<p> +Also, the QValueList (and QMap - not shown) type needs a qualifier - a Python list +type doesn't know (or care) what the type of its elements is. +</p> +<p> +Other types (QString, QCString) are uniquely typed, so no modifier is needed. +</p> +<p> +While it may change in the future, dcop_add right now retains the variable argument lists. +You can handle this in your own code easily if you import "numericTypes" and +"stringTypes" from dcopext as shown above. The following code will sort things out: +</p> + +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> +# atype is the type of the argument being processed (as a string) +# value is the object being packed into the QByteArray + +if atype in numericTypes: + dcop_add (s, value, atype) +elif atype in stringTypes and isinstance (value, str): + dcop_add (s, eval ("%s('%s')" % (atype, value))) +elif atype.startswith ("QMap") or atype.startswith ("QValueList"): + dcop_add (params, value, atype) +else: + dcop_add (s, value) + +</PRE></TD></TR></TABLE> +<p> +At least in DCOP related applications, all of the necessary type information is always +easily available. The first if clause above processes numeric types; the second if +clause allows you to use Python strings in place of Qt's QString or QCString types; the +third if clause handles QValueList and QMap based types; the else clause handles +everything else. +</p> +<p> +Unpacking a QByteArray is simpler - dcop_next always takes a QDataStream instance and +a type name string. The code below assumes the same set of imports as above: +</p> +<TABLE BORDER="0" BGCOLOR="#E0E0E0" WIDTH="100%"> +<TR><TD><PRE CLASS="PROGRAMLISTING"> + +# b is a QByteArray to be unpacked +s = QDataStream (b, IO_ReadOnly) + +i1 = dcop_next (s, "long") +d1 = dcop_next (s, "double") +t1 = dcop_next (s, "QString") +x1 = dcop_next (s, "QCString") +l1 = dcop_next (s, "QValueList<QCString>") + +</PRE></TD></TR></TABLE> +<p> +Of course the type specified in dcop_next to unpack the object must match the type of +the object originally packed, and must happen in the same order (you can't use this to cast or convert types). i1, d1, etc +should contain the same values as i, d, etc above. +</p> +<p> +The types that dcop_add/dcop_next can handle are the same types listed in the dcopext +section above. +</p> +<h2>Thanks</h2> +<p> +The code for dcopext and dcopexport is based on pydcop.py and pcop.cpp written by Torben Weis +and Julian Rockey. It's available in the dcoppython/ section of the kde-bindings source code, +and can be used to implement DCOP communication without using PyQt or PyKDE. +</p> + +<DIV CLASS="NAVFOOTER"> +<HR ALIGN="LEFT" WIDTH="100%"> +<TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" > +<TR> +<TD WIDTH="33%" ALIGN="left" VALIGN="top"><A HREF="examples.html" ACCESSKEY="P">Prev</A></TD> +<TD WIDTH="34%" ALIGN="center" VALIGN="top"><A HREF="index.html" ACCESSKEY="H">Home</A></TD> +<TD WIDTH="33%" ALIGN="right" VALIGN="top"><A HREF="limits.html" ACCESSKEY="N">Next</A></TD> +</TR> +<TR> +<TD WIDTH="33%" ALIGN="left" VALIGN="top">Templates and Example Programs</TD> +<TD WIDTH="34%" ALIGN="center" VALIGN="top"> </TD> +<TD WIDTH="33%" ALIGN="right" VALIGN="top">General Limitations</TD> +</TR> +</TABLE> +</DIV> + +</body> +</html> |