Porting from PyQt to PySide

I recently put together a small project in PyQt, but the license is something that makes me a bit unsettled. I wanted to release the sources under the MIT or BSD licenses, but the requirements of the GPL prohibit carrying out the spirit of either of these, at least in terms of practicality (no downstream commercial use). Thus, I’ve been somewhat torn: I appreciate the GPL, but I more deeply appreciate the MIT/BSD license family for the greater extent of freedom they afford. I disagree that allowing commercial use somehow precludes “freedom” because developers have to eat–and for that matter, I’ll probably purchase a PyQt license at some point in the future, if needed, because it is actively maintained by a small business.

Since starting the project, I rediscovered PySide, an LGPL-licensed alternative to PyQt that acts mostly as a drop-in replacement for the latter. There are some differences that require minor changes to your sources before it can act as a complete replacement, but they’re so minimal as to be of no consequence. Of the ones that I’ve encountered, I’ll document them here since it may not be immediately obvious (although some of the easier cases are).

Signal() and Slot() declarations

PySide’s signal and slot declarations are identical (more or less) to PyQt’s with the exception that they lack a “pyqt” prefix. Thus, the following code:

class MyDialog (QtGui.Dialog):
    progressFinished = QtCore.pyqtSignal()

Must be rewritten as:

class MyDialog (QtGui.Dialog):
    progressFinished = QtCore.Signal()

Likewise, @QtCore.pyqtSlot() decorators are supported by PySide but must be rewritten as @QtCore.Slot(). To this extent, PySide eliminates a handful of unnecessary characters and provides more obvious translation from C++.

setCheckState doesn’t accept integers

PyQt is somewhat more forgiving when passed Python data types and will usually attempt to do the right thing. PySide expects the bindings to follow their C++ cousins more or less exactly, including setting a checkbox’s state via setCheckState(). Thus, checkbox states cannot simply be assigned with the integers 0, 1, or 2 as is possible in PyQt. Instead, the developer must use QtCore.Qt.CheckState.Checked, QtCore.Qt.CheckState.PartiallyChecked, or QtCore.Qt.CheckState.Unchecked. This code does not appear to be compatible between PyQt and PySide.

Python data types may behave unexpectedly in signals

Similar to CheckState, signal arguments are not automatically wrapped (or converted) from Python data types to C++ data types. In one particular case I encountered, larger integers may generate “overflow” errors when exceeding the boundaries of a signed 32-bit integer (2.1 billion, or thereabouts). The only solution in this case is to declare the signal arguments as a Python object as per this example. Other incompatibilities may exist in signal declarations, but this is the one that I encountered fairly early on. Lists and dictionaries are correctly wrapped and behave as expected.

Most other behaviors (threads included) are identical between PyQt and PySide, which is useful for more complicated applications that require offloading activities to one or more threads and error messages appear similar enough (for the most part). More importantly, PySide is a mature enough alternative to PyQt for most uses, although I’ve yet to try it under Windows. It appears binary-only installation is supported on that platform.

No comments.
***