¿Hacer el complemento QGIS python para ambas versiones 2.xy 3.x?

12

Estoy en el proceso de migrar un complemento QGIS python de QGIS 2a QGIS 3, y explorar varios recursos.

No está claro si es posible tener el complemento compatible con ambas versiones o si es necesario dos manejadores para las versiones del complemento.

El problema que encontré hasta ahora es cómo administrar la importación de PyQt (PyQt4 / PyQt5).

sigeal
fuente

Respuestas:

18

Documentación

Aquí puede encontrar qué hay de nuevo y qué hay de nuevo en la API PyQGIS .
Para obtener detalles sobre cómo portar Python2 a Python3, vaya allí

Puede encontrar algunos detalles sobre las pruebas de QGIS2 a QGIS3 en esta pregunta: ¿ Escribir pruebas automatizadas para complementos QGIS?

Y aquí encontrará un interesante documento de OpenGis.ch sobre las herramientas de migración.

¿Qué cambiará en mi código?

De hecho, debe cambiar el código del complemento que no está preparado para pasar a través de una nueva versión.

Obtiene qgis.utils.QGis.QGIS_VERSION_INT función que se realiza para verificar la versión QGIS. Esto es útil cuando una función está en desuso. Por ejemplo setSelectedFeaturesdesde 2.16.

Por ejemplo con el uso de la ifdeclaración:

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

Es lo mismo sobre el PyQtobjeto que importa bajo su módulo. Si necesita compatibilidad, el precio es escribir más líneas de código (el código con la función QGIS2 y el código con las funciones QGIS3 Y también el código para verificar la versión y las capacidades para importar nuevas bibliotecas).

Acerca de las bibliotecas PyQt

El PyQt5 no es compatible con PyQt4; Hay varios cambios significativos en PyQt5. Sin embargo, no es muy difícil ajustar el código anterior a la nueva biblioteca. Las diferencias son, entre otras, las siguientes:

  • Los módulos de Python han sido reorganizados. Algunos módulos se han eliminado (QtScript), otros se han dividido en submódulos (QtGui, QtWebKit).

  • Se han introducido nuevos módulos, incluidos QtBluetooth, QtPosition o Enginio.

  • PyQt5 solo admite la señal de nuevo estilo y las ranuras handlig. Las llamadas a SIGNAL () o SLOT () ya no son compatibles. PyQt5 no admite ninguna parte de la API de Qt que esté marcada como obsoleta u obsoleta en Qt v5.0.

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

Aquí hay algunos ejemplos de cambios en su declaración from / import:

Recuerde que con PyQt4 tenía que buscar en el documento de la API:
por ejemplo,
módulo
PyQT4 QtCore Módulo PyQT4 QtGui

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

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

Y con PyQt5 ahora debe buscar en el documento de esas API:
módulo
PyQt5 QtCore Módulo PyQt5 QtGui

para que se convierta en:

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

Tenga en cuenta que :

El módulo QtGui se ha dividido en submódulos. El módulo QtGui contiene clases para la integración del sistema de ventanas, manejo de eventos, gráficos 2D, imágenes básicas, fuentes y texto. También contiene un conjunto completo de enlaces OpenGL y OpenGL ES (consulte Soporte para OpenGL ). Los desarrolladores de aplicaciones normalmente lo usarían con API de nivel superior, como las contenidas en el módulo QtWidgets.

¡Y PyQt5 solo admite la nueva señal de estilo y ranuras handlig! echar un vistazo a esta página para entender cómo utilizar pyqtSignal, connecty eobjeto de evento en lugar de uso SIGNAL.

Hazlo compatible

Entonces, con la compatibilidad entre PyQt4 / PyQt5 (y QGIS2 / QGIS3 también) debe probar / excepto la importación antes de usar la biblioteca 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

Y no olvide que necesita cambiar también alguna función específica en su código agregando la declaración try / except o if.

Hugo Roussaffa - GeoDatup
fuente
2
Gran respuesta, algo que será de gran ayuda es reemplazar cualquier primera from PyQt4.QtCore import *con from PyQt4.QtCore import QSomething, QWhatever, QElse, esto hará que el script de migración de hacer el último paso correctamente (incluidos los ajustes requeridos en los módulos cambiados), por lo que no tratar -excepto son necesarias las importaciones.
Matthias Kuhn el
tienes razón, solía * para hacerlo simple, pero lo cambiaré, gracias por tus comentarios
Hugo Roussaffa - GeoDatup
este tema es el lugar perfecto para decirle a la gente que no use las importaciones *, porque aquí realmente hace la diferencia
Matthias Kuhn
@Hugo: respuesta muy detallada, de hecho, ayudó mucho a comenzar. Agregaré el complemento qgis2compat a los numerosos recursos útiles ya citados.
sigeal
Esa es una buena idea. Puedes editar la respuesta como quieras. Gracias por los comentarios
Hugo Roussaffa - GeoDatup
2

Intenta algo así:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4
Miguel
fuente
Esto podría funcionar para algunas cosas aisladas, pero a menudo no funcionará como una solución genérica.
Matthias Kuhn el
1

Acabo de terminar de portar un complemento QGIS Python para que ahora sea compatible con las versiones 2.xy 3.x QGIS. Aquí está mi experiencia:

Sobre todo intenté confiar en la versión QGIS. Pero incluso la clase que tenía la versión fue un poco renombrada. Así que primero lo hice

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

y luego hacer cheques

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

Después de implementar una versión final, noté que también es necesario portar un archivo resources.pyque se crea automáticamente pyrcc5. De lo contrario, el complemento se descompondrá en 2.x. Entonces cambié su línea

from PyQt5 import QtCore

a

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

Parecía que funcionaba. Hice un lanzamiento oficial y pensé que era eso. Solo entonces descubrí esta secuencia:

Instale mi complemento en QGIS 2.18, cierre QGIS, abra QGIS agan y luego abra Python Console dentro de QGIS -> ¡Todo QGIS se bloqueará instantáneamente!

Después de algunas pruebas, descubrí que la razón era este pequeño cambio en lo resources.pyescrito anteriormente. No soy experto en bibliotecas QGIS Python pero mi explicación es la siguiente:

Cuando abro QGIS mi plugin se inicializa. Intentar hacerlo from PyQt5 import QtCoreprovoca algunos cambios en el "flujo de trabajo" de QGIS antes de que la versión incorrecta de PyQt genere una excepción (fue una RuntimeError). Cuando inicio Python Console, esos cambios hacen que QGIS se bloquee.

Al final decidí una solución diferente. Como QGIS 2 usa Python 2.7 y QGIS 3 usa Python 3, simplemente siempre hago verificaciones para la versión de Python .

from sys import version_info

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

Esto evita todos los intentos de importación potencialmente dañinos. Mi complemento ahora funciona en ambas versiones de QGIS sin ningún problema.

AleksMat
fuente