Some hints for porting QGIS 2 plugins to the new API of QGIS 3.

  • use the 2to3 scripts, in a low bandwidth scenario (impatience counts also) one can use Gitzip or the like to download only the scripts instead of the whole repo (2to3 as well as qgis_fixes directory is needed)
  • usage is like 2to3 path/to/QGIS/scripts/2to3 /path/to/your/plugin; -w will write the changes while creating .bak files for backup
  • correct star imports (most likely PyQt4.*) like this (the 2to3 script is not able to do this):
    • grep -rl 'path_to_QGIS3_plugins/yourplugin/' -e 'PyQt4\.' | grep "\.bak" -v | grep "cache" -v | xargs sed -i 's/PyQt4/PyQt5/g'
    • explanation:
      • -r = recursive
      • -l = list files
      • 1st pipe = don't include the bak files generated by 2to3
      • 2nd pipe = don't include cache
      • 3rd pipe = take files from grep and replace PyQt4 with PyQt5
  • recompile the resource file with pyrcc5

    • pyrcc5 -o resources_rc.py resources.qrc
  • recompile ui files with pyuic5

    • find . -name \*.ui -type f | xargs basename -s .ui | xargs -I {} pyuic5 -o ./view/Ui_{}.py ./view/{}.ui --from-imports
    • explanation:
      • find all ui files
      • get basename to manipulate it
      • run xargs with replace mode; {} will be the basename; Ui_{} is the .py file; --from-imports argument to make a proper import (QGIS does not seem to handle a simple import resources_rc too well)
  • add QtWidgets import to .py files containing QDialog (other classes could be affected, too; like QWidget or QMessagebox)

    • grep -rl '.' -e 'QDialog' | grep "\.bak" -v | grep "cache" -v | grep "\.ui" -v | grep "Ui_" -v | xargs sed -i '3i from PyQt5.QtWidgets import *'
    • explanation:
      • get all files with QDialog; do not handle “.bak” files; do not handle “cache” files; do not handle “.ui” files; do not handle “Ui_…” files; insert import in 3rd line (because 2to3.py likely introduced two “future” imports at the top)
  • common API breaks for this project:

    • QgsDataSourceURI became QgsDataSourceUri
    • QgsLegendInterface with its currentLayer() is no more, instead QgsMapLayerComboBox is used
    • All methods taking or returning QGis::WkbType have been changed to use QgsWkbTypes::Type; so for example, Qgis.Point becomes QgsWkbTypes.Point (QGIS core library import is needed)
    • QgsRubberBand expects QgsWkbTypes::GeometryType for reset() (instead of Qgis.Point)
    • QgsMapLayerRegistry becomes QgsProject
    • selectedFeaturesIds() replaced by selectedFeatureIds()