Friday, 24 June 2016

pyqgis - Make QGIS python plugin for both versions 2.x and 3.x?


I am in the process of migrating a QGIS python plugin from QGIS 2 to QGIS 3, and browsing various resources.


It's not clear if it's possible to have the plugin compatible with both versions or if it's necessary two handle to plugin versions.



The problem I hit so far is how to manage PyQt import (PyQt4/PyQt5) ?



Answer




Here you can find what is new and what is break under the PyQGIS API.
To get details about how to port Python2 to Python3 go there


You can find some detail about testing from QGIS2 to QGIS3 on this question :Writing automated tests for QGIS plugins?


And you will find an interesting OpenGis.ch's paper here about the migrations tools.



In fact, you need to change code of plugin that are not prepared to pass throught a new version.


You get qgis.utils.QGis.QGIS_VERSION_INT function which is made to check the QGIS version. This is usefull when a function is deprecabled . For exemple setSelectedFeatures since 2.16.



By exemple with the use of if statement :


if qgis.utils.QGis.QGIS_VERSION_INT < 21600 :
joinLayer.setSelectedFeatures( [ f.id() for f in request ] )
else:
joinLayer.selectByIds( [ f.id() for f in request ] )

It's the same about PyQt object you import under your module. If you need compatibility, the price is to wrote more code line (the code with QGIS2 function and the code with QGIS3 functions AND also the code for checking the version and the capabilities to import new libraries ).




The PyQt5 is not backward compatible with PyQt4; there are several significant changes in PyQt5. However, it is not very difficult to adjust older code to the new library. The differences are, among others, the following:





  • Python modules have been reorganized. Some modules have been dropped (QtScript), others have been split into submodules (QtGui, QtWebKit).




  • New modules have been introduced, including QtBluetooth, QtPositioning, or Enginio.



  • PyQt5 supports only the new-style signal and slots handlig. The calls to SIGNAL() or SLOT() are no longer supported. PyQt5 does not support any parts of the Qt API that are marked as deprecated or obsolete in Qt v5.0.




source : (http://zetcode.com/gui/pyqt5/introduction/)


Here is some exemples of changes into your from/import statement:


Remember with PyQt4 you had to look on the API's doc:
for exemple
PyQT4 QtCore module
PyQT4 QtGui module


from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt, QObject, SIGNAL

from PyQt4.QtGui import QAction, QIcon, QDialog, QFormLayout


And with PyQt5 you have now to look on those API's doc :
PyQt5 QtCore module
PyQt5 QtGui module


so that become :


from PyQt5.QtCore import QSettings, QTranslator, QVersionNumber, QCoreApplication, Qt, QObject, pyqtSignal 
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QDialog, QFormLayout

Note that :




QtGui module has been split into submodules. The QtGui module contains classes for windowing system integration, event handling, 2D graphics, basic imaging, fonts and text. It also containes a complete set of OpenGL and OpenGL ES bindings (see Support for OpenGL). Application developers would normally use this with higher level APIs such as those contained in the QtWidgets module.



And PyQt5 supports only the new-style signal and slots handlig! have a look to this page to understand how to use pyqtSignal, connect and e event object instead of use SIGNAL.


Make it compatible


So with compatibility between PyQt4/PyQt5 (and QGIS2 / QGIS3 as well) you need to try/except the import before use the pyQt5 librarie.


try:
from PyQt5.QtCore import QSettings, QTranslator, QVersionNumber, QCoreApplication, Qt, QObject, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QAction, QDialog, QFormLayout


except:
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt, QObject, SIGNAL
from PyQt4.QtGui import QAction, QIcon, QDialog, QFormLayout

And don't forget that you need to change also some specific function under your code by adding try/except or if statement.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...