pycda and qcda - femanov/pycx4 GitHub Wiki
User's manual
general notes
There are CX main loop and Qt versions of extension, named pycda and qcda(q4cda/q5cda), wich provide almost the same API. There are few differences:
-
Import the one you need. Use
import pycx4.pycda as cda
for CX main loop based module orimport pycx4.qcda as cda
for Qt4/Qt5 versions. Modules q4cda/q5cda are binary therefor it's not possible to make them a single portable Qt module. Since we need to make apps compatible with both Qt versions there are pure python qcda submodule which looks which PyQt is imported or try to import any version, then load appropriate qXcda to qcda namespace. -
pycda brings main_loop and break_ wrapper functions to control CX scheduler main loop, while with qcda you need to take it from pyqt.
-
pycda implements Timer and Signal classes - Qt-like ones for CX scheduler
-
Run application main loop (event loop):
for PyQt app:
from PyQt5 import QtCore
app = QtCore.QCoreApplication(sys.argv)
(...your code here...)
app.exec_()
for console app:
Need to rewrite following
import pycx4.scheduler as sl
(...your code here...)
sl.main_loop()
- Signals are
pyqtSignal
for Qt-version, andsl.Signal
for console version.sl.Signal
interface is likepyqtSignal
one - connect, disconnect and emit with the same usage way. But twosl.Signal
can't be directly connected andsl.Signal
always emitted with one parameter (which is python's object - used to pass reference to sender). This similarity made in order to make console code compatible to Qt's one and avoid possible programmer confusion.
Current implementation of sl.Signal
quite low level and performance-optimized. It don't keep a python's references to connected functions, and check if the function is callable only when you connecting it. Destruction of connected function will crash you program.
pycda few times faster than qcda depending on test conditions and hardware
Work logic, difference with C cda
C cda provide a set of functions to operate with contecxts and channels. First you have to create context which stores default name prefix and has some servers-timing callbacks. Than you can populate context with channels - i/o units which give you interface to data (read/write, callback for events like data update).
This logic propagated to Python, but there are few differences:
- Context and channels are level higher then corresponding entities from C. They automatize data acquisition which is manual at C cda level.
- Context is'n required in python. If you create channel without context default context is used, with default name prefix 'cx::' - using CXv4 protocol by default.
- C cda don't makes a difference in channel kind, but pycda has three kinds of channels: scalar channel, scalar double channel and vector channel. Channels have almost the same interface, but internal work significantly different for each case.
- cda formulas are not implemented yet.
module reference
####pycda specific reference
main_loop()
- run CX scheduler main loop. Use it to start main loop of pycda app.
break_()
- break CX scheduler main loop. CX sheduler do not know about python's sys.exit(), and will ignore it. Use this function instead
Signal
- multi-callback-handler class. it's interface looks like pyqtSignals, but there are some significant differences. For simple use there are connect(python-callable)
and disconnect(python-callable)
methods.
pycda and qcda common classes and functions
Context
- cda context class
DChan(name, context=None)
- scalar double channel class
Chan(name, context=None, dtype=CXDTYPE_DOUBLE)
- general scalar channel class
VChan(name, context=None, dtype=CXDTYPE_DOUBLE, max_nelems=1)
- vector channel class
Common for all channels:
val
- a channel last-known value (will return corresponding python's type or numpy array for vchan)
time
- server provided update time, int64 in microseconds from unix epoch
setValue(value)
- function to set a value (number for scalars or numpy array for vchan)
valueMeasured, valueChanged
- signals to connect your callbacks
CX dtype - is a specially coded int, is's better to use following constants:
CXDTYPE_INT8
CXDTYPE_INT16
CXDTYPE_INT32
CXDTYPE_INT64
CXDTYPE_UINT8
CXDTYPE_UINT16
CXDTYPE_UINT32
CXDTYPE_UINT64
CXDTYPE_SINGLE
CXDTYPE_DOUBLE
CXDTYPE_TEXT
CXDTYPE_UCTEXT
There are also some cython-only classes (python-unusable):
'CdaObject' - generalized cda object with callback data keeping. Base class for Context and BaseChan.
'BaseChan' - base class for all channel classes.
Examples:
simple pycda example:
#!/usr/bin/env python
import pycx.pycda as cda # Let's import pycda
# this makes python interpreter exit on Ctrl-C
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
# defining a collback or slot does'n matter how to name it
# function accepts on argument - a channel which sends this signal
# really it's a pointer to channel
def printval(chan):
print chan.val
chan = cda.Chan("localhost:1.name.0") # register a channel with given name
chan.valueMeasured.connect(printval) # connect our callback to channel's signal
cda.main_loop() # run main loop
The same example for Qt:
#!/usr/bin/env python
import sys
from PyQt4 import QtCore # import PyQt
import pycx.qcda as cda # import qcda
# this makes python interpreter exit on Ctrl-C
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
# defining a collback or slot does'n matter how to name it
# function accepts on argument - a channel which sends this signal
# really it's a pointer to channel
def printval(chan):
print chan.val
app = QtCore.QCoreApplication(sys.argv) # first you need Qt app
chan = cda.Chan("localhost:1.name.0") # register a channel with given name
chan.valueMeasured.connect(printval) # connect our collback to channel's signal
sys.exit(app.exec_()) # run main loop