Faça o plugin QGIS python para as versões 2.xe 3.x?

12

Estou migrando um plug-in python QGIS de QGIS 2para QGIS 3e navegando em vários recursos.

Não está claro se é possível ter o plug-in compatível com as duas versões ou se é necessário dois identificadores para as versões do plug-in.

O problema que encontrei até agora é como gerenciar a importação do PyQt (PyQt4 / PyQt5)?

sigeal
fonte

Respostas:

18

Documentação

Aqui você pode encontrar o que há de novo e o que está quebrado na API do PyQGIS .
Para obter detalhes sobre como portar o Python2 para o Python3, acesse

Você pode encontrar alguns detalhes sobre os testes do QGIS2 ao QGIS3 nesta pergunta: Gravando testes automatizados para plug-ins do QGIS?

E você encontrará um artigo interessante do OpenGis.ch aqui sobre as ferramentas de migração.

O que mudará para o meu código

De fato, você precisa alterar o código do plugin que não está preparado para passar por uma nova versão.

Você obtém a função qgis.utils.QGis.QGIS_VERSION_INT que é feita para verificar a versão do QGIS. Isso é útil quando uma função está obsoleta. Por exemplo setSelectedFeaturesdesde 2.16.

Por exemplo, com o uso da ifdeclaração:

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

É o mesmo sobre o PyQtobjeto que você importa no seu módulo. Se você precisar de compatibilidade, o preço é escrever mais linha de código (o código com a função QGIS2 e o código com as funções QGIS3 E também o código para verificar a versão e os recursos para importar novas bibliotecas).

Sobre as bibliotecas PyQt

O PyQt5 não é compatível com o PyQt4; Existem várias mudanças significativas no PyQt5. No entanto, não é muito difícil ajustar o código antigo para a nova biblioteca. As diferenças são, entre outras, as seguintes:

  • Módulos Python foram reorganizados. Alguns módulos foram descartados (QtScript), outros foram divididos em submódulos (QtGui, QtWebKit).

  • Novos módulos foram introduzidos, incluindo QtBluetooth, QtPositioning ou Enginio.

  • O PyQt5 suporta apenas o novo estilo de sinal e slots de mão. As chamadas para SIGNAL () ou SLOT () não são mais suportadas. O PyQt5 não suporta nenhuma parte da API do Qt marcada como obsoleta ou obsoleta no Qt v5.0.

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

Aqui estão alguns exemplos de alterações na sua declaração de / importação:

Lembre-se, com o PyQt4, você teve que procurar no documento da API:
por exemplo,
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

E com o PyQt5, agora você deve consultar o documento da API:
Módulo
PyQt5 QtCore Módulo PyQt5 QtGui

para que se torne:

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

Observe que :

O módulo QtGui foi dividido em submódulos. O módulo QtGui contém classes para integração de sistema de janelas, manipulação de eventos, gráficos 2D, imagens básicas, fontes e texto. Ele também contém um conjunto completo de ligações OpenGL e OpenGL ES (consulte Suporte para OpenGL ). Os desenvolvedores de aplicativos normalmente usariam isso com APIs de nível superior, como as contidas no módulo QtWidgets.

E o PyQt5 suporta apenas o novo estilo de sinal e slots! ter um olhar para esta página para entender como usar pyqtSignal, connecte eobjeto de evento em vez de uso SIGNAL.

Torná-lo compatível

Portanto, com a compatibilidade entre PyQt4 / PyQt5 (e QGIS2 / QGIS3 também), você precisa tentar / exceto a importação antes de usar a 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

E não esqueça que você precisa alterar também alguma função específica no seu código adicionando a instrução try / except ou if.

Hugo Roussaffa - GeoDatup
fonte
2
Grande resposta, algo que vai ajudar muito é a primeira a substituir qualquer from PyQt4.QtCore import *com from PyQt4.QtCore import QSomething, QWhatever, QElse, isso fará com que o script de migração fazer o último passo corretamente (incluindo ajustes necessários onde os módulos alterados), por isso não tente-exceto são necessários importações.
Matthias Kuhn
você está certo, eu usei * para simplificar, mas vou mudar isso, obrigado pelo seu feedback.
Hugo Roussaffa - GeoDatup 31/10
este tema é o lugar perfeito para dizer às pessoas para não usar -Importação *, porque aqui ele realmente faz a diferença
Matthias Kuhn
@ Hugo: Resposta muito detalhada, de fato, ajudou muito a começar. Vou adicionar o plugin qgis2compat aos inúmeros recursos úteis já citados.
Sigile #
Que ótima ideia. Você pode editar a resposta como quiser. Obrigado pelo feedback
Hugo Roussaffa - GeoDatup
2

Tente algo assim:

try:
    # action for QGIS 3/PyQt5
except:
    # action for QGIS 2/PyQt4
Mike
fonte
Isso pode funcionar para algumas coisas isoladas, mas geralmente não funciona como uma solução genérica.
Matthias Kuhn
1

Acabei de portar um plug-in QGIS Python para que ele agora suporte as versões 2.x e 3.x QGIS. Aqui está a minha experiência:

Principalmente, tentei confiar na versão QGIS. Mas mesmo a classe que detinha a versão foi um pouco renomeada. Então eu fiz primeiro

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

e depois fazer cheques

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

Depois de implantar uma versão final, notei que um arquivo resources.pycriado automaticamente por pyrcc5também precisa ser portado. Caso contrário, o plug-in continuará quebrando no 2.x. Então eu mudei sua linha

from PyQt5 import QtCore

para

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

Parecia que funcionava. Fiz um lançamento oficial e pensei que era isso. Só então eu descobri esta sequência:

Instale meu plug-in no QGIS 2.18, feche o QGIS, abra o QGIS novamente e abra o Python Console no QGIS -> Todo o QGIS falhará instantaneamente!

Após alguns testes, descobri o motivo dessa pequena alteração, resources.pyescrita acima. Não sou especialista em bibliotecas QGIS Python, mas minha explicação é a seguinte:

Quando abro o QGIS, meu plug-in é inicializado. A tentativa de fazer from PyQt5 import QtCorecausa algumas alterações no "fluxo de trabalho" do QGIS antes que a versão incorreta do PyQt crie uma exceção (era a RuntimeError). Quando inicio o Python Console, essas alterações causam uma falha no QGIS.

No final, decidi por uma solução diferente. Como o QGIS 2 usa o Python 2.7 e o QGIS 3 usa o Python 3, eu sempre faço verificações da versão do Python .

from sys import version_info

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

Isso evita todas as tentativas de importação potencialmente prejudiciais. Meu plugin agora funciona nas duas versões do QGIS sem problemas.

AleksMat
fonte