Crea un plug-in QGIS per Python per entrambe le versioni 2.xe 3.x?


12

Io sono nel processo di migrazione un plugin QGIS Python da QGIS 2a QGIS 3, e la navigazione varie risorse.

Non è chiaro se è possibile avere il plug-in compatibile con entrambe le versioni o se è necessario gestire due versioni del plug-in.

Il problema che ho riscontrato finora è come gestire l'importazione PyQt (PyQt4 / PyQt5)?

Risposte:


18

Documentazione

Qui puoi trovare le novità e le interruzioni nell'API PyQGIS .
Per ottenere dettagli su come trasferire Python2 su Python3, vai lì

Puoi trovare alcuni dettagli sui test da QGIS2 a QGIS3 su questa domanda: Scrivere test automatizzati per i plug-in QGIS?

E qui troverai un interessante documento di OpenGis.ch sugli strumenti di migrazione.

Cosa cambierà nel mio codice

In effetti, è necessario modificare il codice del plug-in che non è pronto per passare a una nuova versione.

Si ottiene qgis.utils.QGis.QGIS_VERSION_INT funzione che è fatto per verificare la versione di QGIS. Ciò è utile quando una funzione è disabilitata. Ad esempio setSelectedFeaturesdal 2.16.

Per esempio con l'uso della ifdichiarazione:

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

Lo stesso PyQtvale per l' oggetto che importi sotto il tuo modulo. Se hai bisogno di compatibilità, il prezzo è quello di scrivere più righe di codice (il codice con la funzione QGIS2 e il codice con le funzioni QGIS3 E anche il codice per controllare la versione e le capacità per importare nuove librerie).

Informazioni sulle librerie PyQt

PyQt5 non è retrocompatibile con PyQt4; ci sono diverse modifiche significative in PyQt5. Tuttavia, non è molto difficile adattare il codice precedente alla nuova libreria. Le differenze sono, tra le altre, le seguenti:

  • I moduli Python sono stati riorganizzati. Alcuni moduli sono stati eliminati (QtScript), altri sono stati suddivisi in sottomoduli (QtGui, QtWebKit).

  • Sono stati introdotti nuovi moduli, tra cui QtBluetooth, QtPosition o Enginio.

  • PyQt5 supporta solo il segnale di nuova concezione e gli slot handlig. Le chiamate a SIGNAL () o SLOT () non sono più supportate. PyQt5 non supporta alcuna parte dell'API Qt contrassegnata come obsoleta o obsoleta in Qt v5.0.

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

Ecco alcuni esempi di modifiche nella tua dichiarazione from / import:

Ricorda che con PyQt4 hai dovuto consultare il documento dell'API:
ad esempio il
modulo
QtCore PyQT4 Modulo QtGui PyQT4

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

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

E con PyQt5 ora devi guardare il documento di quelle API:
PyQt5 QtCore module
PyQt5 QtGui module

in modo che diventino:

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

Nota che :

Il modulo QtGui è stato suddiviso in sottomoduli. Il modulo QtGui contiene classi per l'integrazione del sistema di finestre, gestione degli eventi, grafica 2D, imaging di base, caratteri e testo. Contiene inoltre un set completo di associazioni OpenGL e OpenGL ES (consultare Supporto per OpenGL ). Gli sviluppatori di applicazioni lo userebbero normalmente con API di livello superiore come quelle contenute nel modulo QtWidgets.

E PyQt5 supporta solo il segnale nuovo stile e slot handlig! avere uno sguardo a questa pagina per capire come usare pyqtSignal, connecte eoggetto evento invece di utilizzo SIGNAL.

Renderlo compatibile

Quindi, con la compatibilità tra PyQt4 / PyQt5 (e anche QGIS2 / QGIS3) è necessario provare / tranne l'importazione prima di utilizzare le librerie 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

E non dimenticare che è necessario modificare anche alcune funzioni specifiche nel codice aggiungendo l'istruzione try / tranne o if.


2
Ottima risposta, qualcosa che sarà di grande aiuto è innanzitutto sostituire qualsiasi from PyQt4.QtCore import *con from PyQt4.QtCore import QSomething, QWhatever, QElse, questo farà sì che lo script di migrazione esegua correttamente l'ultimo passaggio (compresi gli aggiustamenti richiesti in caso di modifica dei moduli), quindi non sono necessarie prove tranne le importazioni.
Matthias Kuhn,

hai ragione ho usato * per mantenerlo semplice, ma lo cambierò, grazie per il tuo feedback
Hugo Roussaffa - GeoDatup

questo argomento è il posto perfetto per dire alla gente di non usare * -import, perché qui fa davvero la differenza
Matthias Kuhn,

@Hugo: risposta molto dettagliata, ha aiutato molto a iniziare. Aggiungerò il plugin qgis2compat alle numerose risorse utili già citate.
sigeal

Questa è un'ottima idea. Puoi modificare la risposta come desideri. Grazie per il feedback
Hugo Roussaffa - GeoDatup,

2

Prova qualcosa del genere:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4

Questo potrebbe funzionare per alcune cose isolate ma spesso non funzionerà come una soluzione generica.
Matthias Kuhn,

1

Ho appena finito il porting di un plug-in QGIS Python in modo che ora supporti entrambe le versioni Qx 2.x e 3.x. Ecco la mia esperienza:

Principalmente ho provato a fare affidamento sulla versione QGIS. Ma anche la classe che detiene la versione è stata leggermente rinominata. Quindi l'ho fatto per la prima volta

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

e quindi effettuare controlli

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

Dopo aver distribuito una versione finale, ho notato che resources.pyè pyrcc5necessario eseguire il porting anche di un file creato automaticamente da . Altrimenti il ​​plugin continuerà a rompersi in 2.x. Quindi ho cambiato linea

from PyQt5 import QtCore

per

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

Sembrava che funzionasse. Ho fatto un rilascio ufficiale e ho pensato che fosse così. Solo allora ho scoperto questa sequenza:

Installa il mio plugin in QGIS 2.18, chiudi QGIS, apri QGIS agan e quindi apri Python Console in QGIS -> L'intero QGIS si bloccherà all'istante!

Dopo alcuni test ho scoperto che il motivo era questo piccolo cambiamento resources.pyscritto sopra. Non sono un esperto di librerie QGIS Python ma la mia spiegazione è la seguente:

Quando apro QGIS il mio plugin viene inizializzato. Il tentativo di fare from PyQt5 import QtCorecausa alcune modifiche al "flusso di lavoro" di QGIS prima che una versione errata di PyQt sollevi un'eccezione (era una RuntimeError). Quando avvio Python Console queste modifiche causano l'arresto anomalo di QGIS.

Alla fine ho deciso per una soluzione diversa. Poiché QGIS 2 utilizza Python 2.7 e QGIS 3 utilizza Python 3, eseguo semplicemente controlli per la versione di Python .

from sys import version_info

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

Questo evita tutti i tentativi di importazione potenzialmente dannosi. Il mio plugin ora funziona senza problemi su entrambe le versioni di QGIS.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.