Faire un plugin QGIS python pour les deux versions 2.x et 3.x?

12

Je suis en train de migrer un plugin python QGIS de QGIS 2à QGIS 3, et la navigation diverses ressources.

Il n'est pas clair s'il est possible d'avoir le plugin compatible avec les deux versions ou s'il faut deux poignées pour les versions de plugin.

Le problème que j'ai rencontré jusqu'à présent est de savoir comment gérer l'importation PyQt (PyQt4 / PyQt5)?

Sigeal
la source

Réponses:

18

Documentation

Ici vous pouvez trouver ce qui est nouveau et ce qui est cassé sous l'API PyQGIS .
Pour obtenir des détails sur la façon de porter Python2 vers Python3, allez-y

Vous pouvez trouver quelques détails sur les tests de QGIS2 à QGIS3 sur cette question: Vous écrivez des tests automatisés pour les plugins QGIS?

Et vous trouverez ici un article intéressant d' OpenGis.ch sur les outils de migration.

Qu'est-ce qui va changer dans mon code

En fait, vous devez changer le code du plugin qui n'est pas prêt à passer par une nouvelle version.

Vous obtenez la fonction qgis.utils.QGis.QGIS_VERSION_INT qui est faite pour vérifier la version de QGIS. C'est utile lorsqu'une fonction est dépréciée. Par exemple setSelectedFeaturesdepuis 2.16.

Par exemple avec l'utilisation de la ifdéclaration:

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 ] )

Il en va de même pour l' PyQtobjet que vous importez sous votre module. Si vous avez besoin de compatibilité, le prix est d'écrire plus de ligne de code (le code avec la fonction QGIS2 et le code avec les fonctions QGIS3 ET aussi le code pour vérifier la version et les capacités d'importer de nouvelles bibliothèques).

À propos des bibliothèques PyQt

Le PyQt5 n'est pas rétrocompatible avec PyQt4; il y a plusieurs changements importants dans PyQt5. Cependant, il n'est pas très difficile d'ajuster l'ancien code à la nouvelle bibliothèque. Les différences sont, entre autres, les suivantes:

  • Les modules Python ont été réorganisés. Certains modules ont été supprimés (QtScript), d'autres ont été divisés en sous-modules (QtGui, QtWebKit).

  • De nouveaux modules ont été introduits, notamment QtBluetooth, QtPositioning ou Enginio.

  • PyQt5 ne prend en charge que le signal de style nouveau et le handlig de slots. Les appels vers SIGNAL () ou SLOT () ne sont plus pris en charge. PyQt5 ne prend en charge aucune partie de l'API Qt marquée comme obsolète ou obsolète dans Qt v5.0.

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

Voici quelques exemples de modifications apportées à votre instruction from / import:

Rappelez-vous qu'avec PyQt4 vous avez dû regarder le doc de l'API:
par exemple
module PyQT4 QtCore module
PyQT4 QtGui

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

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

Et avec PyQt5, vous devez maintenant regarder le document de ces API:
module PyQt5 QtCore module
PyQt5 QtGui

pour devenir:

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

Notez que :

Le module QtGui a été divisé en sous-modules. Le module QtGui contient des classes pour l'intégration du système de fenêtrage, la gestion des événements, les graphiques 2D, l'imagerie de base, les polices et le texte. Il contient également un ensemble complet de liaisons OpenGL et OpenGL ES (voir Prise en charge d'OpenGL ). Les développeurs d'applications l'utilisent normalement avec des API de niveau supérieur telles que celles contenues dans le module QtWidgets.

Et PyQt5 ne prend en charge que le signal de style nouveau et le handlig! jetez un œil à cette page pour comprendre comment utiliser pyqtSignal, connectet eobjet d'événement au lieu de l'utiliser SIGNAL.

Rendez-le compatible

Donc, avec la compatibilité entre PyQt4 / PyQt5 (et QGIS2 / QGIS3 également), vous devez essayer / sauf l'importation avant d'utiliser la bibliothèque pyQt5.

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

Et n'oubliez pas que vous devez également modifier certaines fonctions spécifiques sous votre code en ajoutant l'instruction try / except ou if.

Hugo Roussaffa - GeoDatup
la source
2
Grande réponse, quelque chose qui va grandement aider est d'abord remplacer tout from PyQt4.QtCore import *avec from PyQt4.QtCore import QSomething, QWhatever, QElse, cela va rendre le script de migration font la dernière étape correctement (y compris les ajustements nécessaires lorsque les modules modifiés), donc pas essayer, sauf les importations sont nécessaires.
Matthias Kuhn
vous avez raison j'ai utilisé * pour rester simple, mais je vais changer cela, merci pour vos commentaires
Hugo Roussaffa - GeoDatup
ce sujet est l'endroit idéal pour dire aux gens de ne pas utiliser les importations *, car ici, cela fait vraiment une différence
Matthias Kuhn
@Hugo: Réponse très détaillée en effet, cela a beaucoup aidé au démarrage. J'ajouterai le plugin qgis2compat aux nombreuses ressources utiles déjà citées.
sigeal
Voilà une excellente idée. Vous pouvez modifier la réponse comme vous le souhaitez. Merci pour les commentaires
Hugo Roussaffa - GeoDatup
2

Essayez quelque chose comme ça:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4
Mike
la source
Cela pourrait fonctionner pour certaines choses isolées mais ne fonctionnera pas souvent comme solution générique.
Matthias Kuhn
1

Je viens de terminer le portage d'un plugin QGIS Python afin qu'il prenne désormais en charge les versions QGIS 2.x et 3.x. Voici mon expérience:

Surtout, j'ai essayé de compter sur la version QGIS. Mais même la classe détenant la version a été un peu renommée. J'ai donc d'abord fait

try:
    from qgis.utils import Qgis  # for QGIS 3
except ImportError:
    from qgis.utils import QGis as Qgis  #  for QGIS 2

puis faire des vérifications

if Qgis.QGIS_VERSION >= '3.0':
    # something for QGIS 3
else:
    # something for QGIS 2

Après avoir déployé une version finale, j'ai remarqué qu'un fichier resources.pycréé automatiquement par pyrcc5doit également être porté. Sinon, le plugin continuera de tomber en panne en 2.x. J'ai donc changé de ligne

from PyQt5 import QtCore

à

try:
    from PyQt5 import QtCore
except:
    from PyQt4 import QtCore

Il semblait que cela fonctionnait. J'ai fait une sortie officielle et j'ai pensé que c'était tout. C'est seulement alors que j'ai découvert cette séquence:

Installez mon plugin dans QGIS 2.18, fermez QGIS, ouvrez QGIS agan puis ouvrez la console Python dans QGIS -> Le QGIS entier se bloquera instantanément!

Après quelques tests, j'ai découvert que la raison était ce petit changement resources.pyécrit ci-dessus. Je ne suis pas un expert des bibliothèques QGIS Python mais mon explication est la suivante:

Lorsque j'ouvre QGIS, mon plugin est initialisé. Tenter de le faire from PyQt5 import QtCoreprovoque quelques modifications dans le "workflow" QGIS avant qu'une mauvaise version de PyQt ne déclenche une exception (c'était a RuntimeError). Lorsque je démarre la console Python, ces modifications provoquent le blocage de QGIS.

À la fin, j'ai décidé d'une solution différente. Parce que QGIS 2 utilise Python 2.7 et QGIS 3 utilise Python 3, je vérifie simplement toujours la version Python .

from sys import version_info

if version_info[0] >= 3:
    # something for QGIS 3
else:
    # something for QGIS 2

Cela évite toutes les tentatives d'importation potentiellement nuisibles. Mon plugin fonctionne désormais sans problème sur les deux versions de QGIS.

AleksMat
la source