Czy zrobić wtyczkę python QGIS dla obu wersji 2.xi 3.x?

12

Jestem w procesie migracji wtyczki QGIS Python od QGIS 2celu QGIS 3i przeglądania różnych zasobów.

Nie jest jasne, czy możliwe jest kompatybilność wtyczki z obiema wersjami, czy też konieczne są dwa uchwyty do wersji wtyczki.

Problem, na który natrafiłem do tej pory, to jak zarządzać importem PyQt (PyQt4 / PyQt5)?

sigeal
źródło

Odpowiedzi:

18

Dokumentacja

Tutaj możesz znaleźć, co nowego, a co psuje się w interfejsie API PyQGIS .
Aby uzyskać szczegółowe informacje na temat sposobu przenoszenia Python2 na Python3, przejdź tam

Szczegółowe informacje na temat testowania z QGIS2 na QGIS3 można znaleźć w tym pytaniu: Pisanie automatycznych testów wtyczek QGIS?

Znajdziesz tu interesujący artykuł OpenGis.ch na temat narzędzi do migracji.

Co zmieni się w mój kod

W rzeczywistości musisz zmienić kod wtyczki, która nie jest przygotowana do przejścia przez nową wersję.

Otrzymasz qgis.utils.QGis.QGIS_VERSION_INT funkcję, która jest wykonana w celu sprawdzenia wersji QGIS. Jest to przydatne, gdy funkcja jest przestarzała. Na przykład setSelectedFeaturesod 2.16.

Na przykład za pomocą ifinstrukcji:

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

To samo dotyczy PyQtobiektu importowanego w ramach modułu. Jeśli potrzebujesz kompatybilności, cena polega na napisaniu większej liczby linii kodu (kod z funkcją QGIS2 i kod z funkcjami QGIS3 ORAZ także kod do sprawdzenia wersji i możliwości importowania nowych bibliotek).

O bibliotekach PyQt

PyQt5 nie jest wstecznie kompatybilny z PyQt4; istnieje kilka istotnych zmian w PyQt5. Jednak dostosowanie starszego kodu do nowej biblioteki nie jest trudne. Różnice są między innymi następujące:

  • Moduły Python zostały zreorganizowane. Niektóre moduły zostały usunięte (QtScript), inne zostały podzielone na submoduły (QtGui, QtWebKit).

  • Wprowadzono nowe moduły, w tym QtBluetooth, QtPositioning lub Enginio.

  • PyQt5 obsługuje tylko obsługę sygnałów i gniazd w nowym stylu. Wywołania SIGNAL () lub SLOT () nie są już obsługiwane. PyQt5 nie obsługuje żadnych części interfejsu API Qt, które są oznaczone jako przestarzałe lub nieaktualne w Qt v5.0.

źródło: ( http://zetcode.com/gui/pyqt5/introduction/ )

Oto kilka przykładów zmian w wyciągu z / import:

Pamiętaj, że z PyQt4 musiałeś przejrzeć dokumentację API:
na przykład
moduł
PyQT4 QtCore moduł PyQT4 QtGui

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

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

A z PyQt5 musisz teraz zapoznać się z dokumentacją API:
PyQt5 Moduł QtCore Moduł
PyQt5 QtGui

aby stały się:

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

Uwaga:

Moduł QtGui został podzielony na submoduły. Moduł QtGui zawiera klasy integracji systemu okienkowego, obsługi zdarzeń, grafiki 2D, podstawowego obrazowania, czcionek i tekstu. Zawiera także pełny zestaw powiązań OpenGL i OpenGL ES (zobacz Obsługa OpenGL ). Twórcy aplikacji zwykle używają tego z interfejsami API wyższego poziomu, takimi jak te zawarte w module QtWidgets.

PyQt5 obsługuje tylko obsługę sygnałów i gniazd w nowym stylu! zajrzyj na tę stronę, aby dowiedzieć się, jak używać pyqtSignal, connecti eobiektu zdarzenia zamiast używać SIGNAL.

Zrób to kompatybilne

Tak więc z kompatybilnością między PyQt4 / PyQt5 (a także QGIS2 / QGIS3) musisz spróbować / oprócz importu przed użyciem biblioteki 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

I nie zapominaj, że musisz zmienić także określoną funkcję w swoim kodzie, dodając instrukcję try / else lub if.

Hugo Roussaffa - GeoDatup
źródło
2
Świetna odpowiedź, coś, co w znacznym stopniu pomaga to najpierw wymienić każdy from PyQt4.QtCore import *z from PyQt4.QtCore import QSomething, QWhatever, QElsebędzie to zrobić skrypt migracji zrobić ostatni krok poprawnie (w tym wymaganych korekt gdzie moduły zmienione), więc nie spróbować z wyjątkiem importu są potrzebne.
Matthias Kuhn
masz rację, użyłem * dla uproszczenia, ale zmienię to, dziękuję za twoją opinię
Hugo Roussaffa - GeoDatup
ten temat jest idealnym miejscem, aby powiedzieć ludziom, aby nie używali * -importów, ponieważ tutaj to naprawdę robi różnicę
Matthias Kuhn
@ Hugo: Rzeczywiście bardzo szczegółowa odpowiedź, bardzo pomogła na początku. Będę dodać qgis2compat plugin do wielu użytecznych zasobów już cytowanych.
sigeal
To świetny pomysł. Możesz edytować odpowiedź, jak chcesz. Dzięki za opinie
Hugo Roussaffa - GeoDatup
2

Wypróbuj coś takiego:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4
Mikrofon
źródło
Może to działać w przypadku niektórych odizolowanych rzeczy, ale często nie będzie działać jako ogólne rozwiązanie.
Matthias Kuhn
1

Właśnie zakończyłem portowanie wtyczki QGIS Python, dzięki czemu obsługuje teraz zarówno wersje QGIS 2.x, jak i 3.x. Oto moje doświadczenie:

Głównie starałem się polegać na wersji QGIS. Ale nawet klasa posiadająca wersję została nieco zmieniona. Więc najpierw to zrobiłem

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

a następnie sprawdź

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

Po wdrożeniu ostatecznej wersji zauważyłem, że plik, resources.pyktóry jest tworzony automatycznie, pyrcc5również musi zostać przeniesiony. W przeciwnym razie wtyczka będzie się rozkładać w wersji 2.x. Więc zmieniłem jego linię

from PyQt5 import QtCore

do

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

Wyglądało na to, że zadziałało. Wydałem oficjalne wydanie i pomyślałem, że o to chodzi. Dopiero wtedy odkryłem tę sekwencję:

Zainstaluj moją wtyczkę w QGIS 2.18, zamknij QGIS, otwórz agan QGIS, a następnie otwórz konsolę Python w QGIS -> Cały QGIS natychmiast się zawiesi!

Po kilku testach dowiedziałem się, że przyczyną była ta niewielka zmiana w resources.pypiśmie powyżej. Nie jestem ekspertem od bibliotek QGIS Python, ale moje wyjaśnienie jest następujące:

Kiedy otwieram QGIS, moja wtyczka zostaje zainicjowana. Próba wykonania from PyQt5 import QtCorepowoduje pewne zmiany w „przepływie pracy” QGIS, zanim niewłaściwa wersja PyQt zgłosi wyjątek (było to a RuntimeError). Kiedy uruchamiam konsolę Python, zmiany te powodują awarię QGIS.

Na koniec zdecydowałem się na inne rozwiązanie. Ponieważ QGIS 2 używa Python 2.7, a QGIS 3 używa Python 3, po prostu zawsze sprawdzam wersję Python .

from sys import version_info

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

Pozwala to uniknąć wszystkich potencjalnie szkodliwych prób importowania. Moja wtyczka działa teraz w obu wersjach QGIS bez żadnych problemów.

AleksMat
źródło