QGIS-Python-Plugin für beide Versionen 2.x und 3.x erstellen?

12

Ich bin dabei, ein QGIS-Python-Plugin von QGIS 2nach zu migrieren QGIS 3und verschiedene Ressourcen zu durchsuchen.

Es ist nicht klar, ob es möglich ist, dass das Plugin mit beiden Versionen kompatibel ist, oder ob es zwei Punkte für Plugin-Versionen gibt.

Das Problem, auf das ich bisher gestoßen bin, ist die Verwaltung des PyQt-Imports (PyQt4 / PyQt5).

Sigeal
quelle

Antworten:

18

Dokumentation

Hier finden Sie unter der PyQGIS-API , was neu ist und was nicht .
Hier erfahren Sie , wie Sie Python2 auf Python3 portieren

Zu dieser Frage finden Sie einige Details zum Testen von QGIS2 auf QGIS3: Automatisierte Tests für QGIS-Plugins schreiben?

Und hier finden Sie einen interessanten Beitrag von OpenGis.ch zu den Migrations-Tools.

Was wird sich in meinem Code ändern

Tatsächlich müssen Sie den Code des Plugins ändern, das nicht für die Weitergabe einer neuen Version vorbereitet ist.

Sie erhalten die Funktion qgis.utils.QGis.QGIS_VERSION_INT , mit der die QGIS-Version überprüft wird. Dies ist nützlich, wenn eine Funktion nicht mehr unterstützt wird. Zum Beispiel setSelectedFeaturesseit 2.16.

Beispiel mit der Verwendung von ifAnweisung:

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

Das Gleiche gilt für PyQtObjekte, die Sie unter Ihrem Modul importieren. Wenn Sie Kompatibilität benötigen, ist der Preis mehr Codezeile zu schreiben (der Code mit QGIS2-Funktion und der Code mit QGIS3-Funktionen UND auch der Code zum Überprüfen der Version und der Möglichkeiten zum Importieren neuer Bibliotheken).

Über PyQt-Bibliotheken

Der PyQt5 ist nicht abwärtskompatibel mit PyQt4; Es gibt einige signifikante Änderungen in PyQt5. Es ist jedoch nicht sehr schwierig, älteren Code an die neue Bibliothek anzupassen. Die Unterschiede sind unter anderem folgende:

  • Python-Module wurden neu organisiert. Einige Module wurden entfernt (QtScript), andere wurden in Submodule aufgeteilt (QtGui, QtWebKit).

  • Es wurden neue Module eingeführt, darunter QtBluetooth, QtPositioning oder Enginio.

  • PyQt5 unterstützt nur die Signal- und Slots-Handlig-Funktion des neuen Stils. Die Aufrufe von SIGNAL () oder SLOT () werden nicht mehr unterstützt. PyQt5 unterstützt keine Teile der Qt-API, die in Qt 5.0 als veraltet oder veraltet markiert sind.

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

Hier einige Beispiele für Änderungen an Ihrer from / import-Anweisung:

Denken Sie daran, dass Sie sich mit PyQt4 das API-Dokument ansehen mussten :
Zum Beispiel das
PyQT4-QtCore-Modul Das
PyQT4-QtGui-Modul

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

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

Und mit PyQt5 müssen Sie sich jetzt das
PyQt5 QtCore-Modul von
PyQt5 QtGui ansehen

so dass werden:

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

Beachten Sie, dass :

Das QtGui-Modul wurde in Submodule aufgeteilt. Das QtGui-Modul enthält Klassen für die Integration von Fenstersystemen, Ereignisbehandlung, 2D-Grafiken, grundlegende Bildbearbeitung, Schriftarten und Text. Es enthält auch einen vollständigen Satz von OpenGL- und OpenGL ES-Bindungen (siehe Unterstützung für OpenGL ). Anwendungsentwickler verwenden dies normalerweise mit APIs höherer Ebenen, wie sie im QtWidgets-Modul enthalten sind.

Und PyQt5 unterstützt nur das neuartige Signal und die handlig-Slots! haben Sie einen Blick auf dieser Seite zu verstehen , wie zu verwenden pyqtSignal, connectund eEreignisobjekt statt Verwendung SIGNAL.

Mache es kompatibel

Um die Kompatibilität zwischen PyQt4 / PyQt5 (und QGIS2 / QGIS3) zu gewährleisten, müssen Sie den Import versuchen / ausnehmen, bevor Sie die pyQt5-Bibliothek verwenden.

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

Und vergessen Sie nicht, dass Sie auch eine bestimmte Funktion in Ihrem Code ändern müssen, indem Sie die Anweisung try / except oder if hinzufügen.

Hugo Roussaffa - GeoDatup
quelle
2
Große Antwort, etwas , das sehr helfen wird, zunächst jede ersetzen from PyQt4.QtCore import *mit from PyQt4.QtCore import QSomething, QWhatever, QElse, wird dies die Migration Skript macht richtig den letzten Schritt tun (einschließlich dem erforderlichen Anpassungen bei denen Module geändert), so dass keine try-except Importen benötigt werden.
Matthias Kuhn
Sie haben Recht, ich habe * verwendet, um es einfach zu halten, aber ich werde das ändern, danke für Ihr Feedback
Hugo Roussaffa - GeoDatup
Dieses Thema ist der perfekte Ort, um den Leuten zu sagen, dass sie * -Importe nicht verwenden sollen, da es hier tatsächlich einen Unterschied macht
Matthias Kuhn
@ Hugo: Sehr detaillierte Antwort, es hat sehr geholfen, loszulegen. Ich werde das qgis2compat- Plugin zu den zahlreichen bereits genannten nützlichen Ressourcen hinzufügen .
Sigeal
Das ist eine großartige Idee. Sie können die Antwort nach Ihren Wünschen bearbeiten. Vielen Dank für das Feedback
Hugo Roussaffa - GeoDatup
2

Versuche so etwas:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4
Mike
quelle
Dies funktioniert möglicherweise für einige isolierte Dinge, ist jedoch häufig keine generische Lösung.
Matthias Kuhn
1

Ich habe gerade ein QGIS-Python-Plugin fertig portiert, sodass es jetzt sowohl die 2.x- als auch die 3.x-QGIS-Versionen unterstützt. Hier ist meine Erfahrung:

Meistens habe ich versucht, mich auf die QGIS-Version zu verlassen. Aber auch die Klasse mit der Version wurde ein bisschen umbenannt. Also habe ich es zuerst getan

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

und dann Schecks machen

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

Nach der Bereitstellung einer endgültigen Version habe ich festgestellt, dass eine Datei resources.py die automatisch von erstellt wird, pyrcc5auch portiert werden muss. Andernfalls wird das Plugin in 2.x nicht mehr funktionieren. Also habe ich die Zeile geändert

from PyQt5 import QtCore

zu

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

Es sah so aus, als hätte es funktioniert. Ich habe eine offizielle Veröffentlichung gemacht und dachte, das ist es. Erst dann habe ich diese Sequenz entdeckt:

Installiere mein Plugin in QGIS 2.18, schließe QGIS, öffne QGIS agan und öffne dann Python Console in QGIS -> Das gesamte QGIS stürzt sofort ab!

Nach einigen Tests stellte ich fest, dass der Grund für diese kleine Änderung in resources.py der obigen Beschreibung liegt. Ich bin kein Experte für QGIS-Python-Bibliotheken, aber meine Erklärung lautet wie folgt:

Wenn ich QGIS öffne, wird mein Plugin initialisiert. Ein Versuch, dies zu tun, from PyQt5 import QtCoreführt zu einigen Änderungen im QGIS - "Workflow", bevor eine falsche PyQt - Version eine Ausnahme auslöst (es war eineRuntimeError ). Wenn ich Python Console starte, führen diese Änderungen zum Absturz von QGIS.

Am Ende habe ich mich für eine andere Lösung entschieden. Da QGIS 2 Python 2.7 und QGIS 3 Python 3 verwendet , überprüfe ich einfach immer die Python-Version .

from sys import version_info

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

Dies vermeidet alle potenziell schädlichen Importversuche. Mein Plugin funktioniert nun problemlos auf beiden QGIS-Versionen.

AleksMat
quelle