1

I have my own C++ library project(with source) written in Qt and it uses QTcpsocket, QUdpSocket, QSerialPort signals and slots.

I would like to support this library in Python as well.

What is the preferred way to do this?

  • Writing a wrapper in Python, if so does it have obstacles?
  • Dont know if PyQt is just for this purpose?
  • Or do you thnink is it better to rewrite the lib in Python by just implementing the logic used in C++ library project.

As this is library is part of a SDK, same applies for supporting QT dll with .NET as well in fact, as a second step after supporting Python.

Example API of Qt.

quint16 SendCommandAsync(CmdBaseSpv1* pcommand,
                         ConnectionArg connectionArg,
                         emitLogOptions::emitLogOption logOption,
                         LogInfo &txLogInfo,
                         LogInfo &rxLogInfo);

I want to call this function from Python. Function parameters CmdBaseSpv1, ConnectionArg, emitLogOption, LogInfo are all Qt classes. Some of these arguments are using the QOBJECT base class.

As you see from the function name; it is an Asynchronous function call. Result will emit a signal so I need to get async result as well.

freewill
  • 1,111
  • 1
  • 10
  • 23
  • What does the API look like? Do you need work with Qt-classes in the user-code? If yes, could you also use python-native classes such as strings and lists instead? – Georg Schölly Jan 24 '17 at 10:09
  • @GeorgSchölly if you mean the QT API, it heavily depends on QT classes and types such as QString, quint32, etc. – freewill Jan 24 '17 at 10:13
  • What about the API of your library? What do the methods / classes look like that you want to expose to Python? – Georg Schölly Jan 24 '17 at 10:17
  • I've edited the question and given an example API. – freewill Jan 24 '17 at 10:57
  • Slightly offtopic, but I would recommend PySide over PyQt for licensing issues (PyQt does not over a LGPL version). – Elijan9 Jan 24 '17 at 10:59

1 Answers1

3

I'll write down what I know about wrapping C++ libraries and will try to source it, but as a huge disclaimer, I have only used this for something very, very simple myself.

You ask about rewriting the library in Python. I would say this depends. If the code is trivial, then I don't see why not. If it is larger and has to be kept up-to-date with other code (as you imply with .Net), I wouldn't. It makes sense to reuse the same code for both.

 My suggestion

From what I see of your code I would try to wrap it using boost::python or SWIG.

How to wrap

The main trouble is going to be to create CmdBaseSpv1, ConnectionArg, etc. in Python.

If you don't need any Qt-classes to instantiate your classes, this should be straightforward. However, in case you need the Qt types inside of Python (e.g. because the constructor of CmdBaseSpv1 requires a QString), your task is a lot more complicated because you need a way to convert a Python-string into a QString. If you can, you should only use stl-types.

Everything in Python

The simplest way to wrap a small C library is to use the cffi module (or ctypes). You can write the full binding in Python. However, this is a lot of manual work if your API is large and can get difficult.

There is another problem: ctypes is only compatible with C, not C++. So you'd need to change your interface to be compatible with C, internally you could still use C++ and Qt.

Wrap by hand

An alternative is to wrap the library calls yourself. You can either do this by using the Python API. There are also a few libraries that help you create the bindings. Boost::python seems especially promising and works with C++.

Binding generators

If your API is very large, you should use a binding generator which parses the C++ code and generates the bindings itself. For example sip is one of them. It is used to create the bindings for the whole Qt library. There are a few binding generators out there, one mentioned in the Python docs is SWIG. PySide uses Shiboken and also has a nice description of it on their website.

SWIG has the additional advantage, that you can create bindings for multiple languages, including C#.

PyQt

PyQt is a binding generated from Qt using sip. You'll probably not need it, unless you need to access the full power of Qt from inside Python. If this is the case, consider using sipfor generating the bindings, so things like the signal-slot mechanism are compatible between your library and PyQt.

Challenges with bindings

Bindings come with a few challenges because Python and C++ are different in some key areas.

Memory-management

Memory management in Python is almost automatic, in C++ you're required to do it manually. For example

def myfunc():
    mywidget = QWidget()

at the end of myfunc() mywidget gets garbage collected. In C++ however

void myfunc() {
    auto mywidget = new QWidget();
}

mywidget is still around. This means that even when inside Python, you need to take care of the C++ memory management. The problems I've seen are memory leaks and dangling pointers. Watch out for this when using callbacks, you don't want Python to garbage collect the callback while C++ thinks it's still alive.

Exceptions

Not all programming languages have exceptions or deal with them the same way. For example, it would be nice if an exception inside C++ can be caught inside Python.

Links to related question

Community
  • 1
  • 1
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • Excellent guide, thank you. My findings after all day work; SWIG is not intented for Qt (I understand that somewhat possible but not easy), SIP is promising, working on it. For .Net wrapping, Qt have built in support I think. i.e.ActiveQt. My work will probably will end with SIP or rewriting SDK for the target platform. SDK is not small in size, but luckily written with isolated parts obeying Seperation Of Concerns pattern. Therefore wont cause too much headache. Cons will be as you stated; updating changes for all platforms will require more work. – freewill Jan 25 '17 at 15:26
  • @freewil: Thank you for the update. I was hoping that you could ignore the whole Qt-dependency altogether for the binding and treat the objects just like regular C++ objects. That's unfortunate, good luck though. – Georg Schölly Jan 25 '17 at 15:39