Make QGIS python plugin for both versions 2.x and 3.x?
Documentation
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.
What will change into my code
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 ).
About PyQt 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.
Try something like that:
try:
# action for QGIS 3/PyQt5
except:
# action for QGIS 2/PyQt4