From 90825e2392b2d70e43c7a25b8a3752299a933894 Mon Sep 17 00:00:00 2001
From: toma
+
+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.
+
+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
+
+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.
+
+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.
+
+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.
+
+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.
+
+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).
+
+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).
+
+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).
+
+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.
+
+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.
+
+Many applications (konqueror, for example) can have several instances running at the same
+time. kdcop would display multiple instances like this:
+
+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.
+
+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).
+
+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).
+
+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.
+
+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:
+
+If a method isn't valid, it's rtype, argtypes and argnames values will all be None.
+DCOP and Extensions
+What Extensions?
+There are three basic extensions added to PyKDE that are not part of KDE itself:
+
+
+Calling DCOP Methods
+Collection the Information
+
+Application/Object/Method Information
+Writing the Code
+
+
+
+import dcopext
+# ! other imports not shown !
+
+app = KApplication ()
+dcop = app.dcopClient ()
+
+d = dcopext.DCOPApp ("kicker", dcop)
+ok, panelPos = d.Panel.panelPosition ()
+
+
+
+
+import dcopext
+# ! other imports not shown !
+
+o = dcopext.DCOPObj ("kicker", dcop, "Panel")
+ok, panelPos = o.panelPosition ()
+
+More on Application Names
+
+Data Types
+The DCOP extensions will support any of the following C++ data types:
+
+
+char short int
+long unsigned char unsigned short
+unsigned int unsigned long uchar
+ushsort uint ulong
+Q_INT32 pid_t float
+double QString QStringList
+QCString KURL KURL::List
+QSize QRect QRegion
+QFont QCursor QPixmap
+QColor QColorGroup QPalette
+QBrush QWidget::FocusPolicy DCOPRef
+QVariant QDate QTime
+QDateTime QImage QKeySequence
+QPen QPicture QPointArray
+QValueList<DCOPRef> QValueList<QCString> QMap<QCString,DCOPRef>
+QMap<QCString,DCOPRef> Other Extension Features
+
+
+
+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: +
+
+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
+
+ |
+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. +
++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. +
++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. +
++The dcop_add function is actually a group of overloaded functions, some of which take +different argument counts. Here are some examples: +
+
+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>")
+
+ |
+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. +
++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. +
++Other types (QString, QCString) are uniquely typed, so no modifier is needed. +
++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: +
+ +
+# 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)
+
+ |
+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. +
++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: +
++ +# 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>") + + |
+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. +
++The types that dcop_add/dcop_next can handle are the same types listed in the dcopext +section above. +
++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. +
+ + + + +