Construindo um complemento com Python

Complementos são uma ótima maneira de estender a funcionalidade do QGIS. Você pode escrever complementos usando Python que podem variar desde adicionar um botão simples até kits de ferramentas sofisticadas. Este tutorial irá descrever o processo envolvido na criação de seu ambiente de desenvolvimento, o desenho da interface do usuário para um plugin e escrever o código para interagir com o QGIS. Por favor, reveja o getting_started_with_pyqgis`tutorial getting_started_with_pyqgis para se familiarizar com o básico.

Visão geral da tarefa

Vamos desenvolver um plugin simples chamado Salvar Atributos que permitirá aos usuários escolher uma camada vetorial e escrever seus atributos para um arquivo CSV.

Obter as Ferramentas

Qt Creator

Qt é um framework de desenvolvimento de software que é usado para desenvolver aplicações que rodam no Windows, Mac, Linux, assim como diversos sistemas operacionais móveis. o QGIS é escrito usando o framework Qt. Para o desenvolvimento de plugins, vamos usar um aplicativo chamado ``Qt Creator <http://doc.qt.io/qtcreator/index.html>`_ para projetar a interface para o nosso plugin.

Faça o download e instale o software Qt Creator pelo SourgeForge

Ligações Python para o Qt

Uma vez que estamos a desenvolver um plugin em Python, é preciso instalar os vínculos python para o Qt. O método para a instalação destes dependerá da plataforma que você está usando. Para a construção de plugins precisamos do ferramenta de linha de comando pyrcc4.

Windows

Faça o download do instalador OSGeo4W rede e escolha Express Desktop install. Instale o pacote QGIS. Após a instalação, você será capaz de acessar a ferramenta pyrcc4 via OSGeo4W Shell.

Mac

Instale o gerenciador de pacotes Homebrew. Instale o pacote PyQt executando o seguinte comando:

brew install pyqt

Linux

Dependendo da sua distribuição, encontre e instale o pacote python-qt4. No Ubuntu e distribuições baseadas no Debian, você pode executar o seguinte comando:

sudo apt-get install python-qt4

Um editor de texto ou um IDE Python

Qualquer tipo de desenvolvimento de software requer um bom editor de texto. Se você já tem um editor de texto favorito ou um IDE (Integrated Development Environment), você pode usá-lo para este tutorial. Caso contrário, cada plataforma oferece uma ampla variedade de opções gratuitas ou pagas para editores de texto. Escolha o que se adapta às suas necessidades.

Este tutorial usa o editor Notepad++ no Windows.

Windows

Notepad++ é um bom editor para o windows. Baixe e ibnstale o Notepad++ em <http://notepad-plus-plus.org/repository/6.x/6.7.5/npp.6.7.5.Installer.exe>`_.

Nota

Se você usa o Notepad++, faz-se de verificar Replace by space at Settings ‣ Preferences ‣ Tab Settings. Python é muito sensível sobre o espaço em branco e esta configuração irá garantir como as tabulações e espaços são tratados adequadamente.

Complemento para construir um complemento

Existe um complemento no QGIS útil chamado `` Plugin Builder`` que cria todos os arquivos necessários e o código clichê para um plugin. Encontre e instale o complemento Plugin Builder. Veja Using Plugins para obter mais detalhes sobre como instalar complementos.

Complemento para recarregar complementos

Este é um outro complemento auxiliar que permite o desenvolvimento iterativo de complementos. Usando este complemento, você pode alterar o código de um complemento e tê-lo refletido no QGIS sem ter que reiniciar o QGIS a cada vez. Encontre e instale o complemento `` Plugin Reloader``. Veja using_plugins para obter mais detalhes sobre como instalar plugins.

Nota

O complemento Recarregar complementos é um plugin experimental. Certifique-se de ter verificado Exibir também complementos experimentais nas configurações do gerenciador de complementos se você não encontrá-lo.

Procedimento

  1. Abra o QGIS. Vá até Complementos‣ Plugin Builder ‣ Plugin Builder.

../_images/116.png
  1. Você vai ver o QGIS Plugin Builder com um formulário. Você pode preencher o formulário com detalhes relativos ao nosso complemento. A Class name será o nome da classe Python contendo a lógica do plugin. Esta será também o nome da pasta que contém todos os arquivos do plugin. Digite SaveAttributes como o nome da classe. A Plugin name é o nome sob o qual o seu plugin será exibido no Gerenciador de complementos. Digite o nome como Save Attributes. Adicione uma descrição no campo Description. O Module name será o nome do arquivo python principal para o plugin. Inseri-lo como save_attributes. Deixe os números de versão como estão. A Text for menu item de menu será como os usuários vão encontrar o seu plugin no menu do QGIS. Inseri-lo como Save Attributes as CSV. Digite seu nome e endereço de e-mail nos campos apropriados. O campo Menu vai decidir onde seu complemento será adicionado no QGIS. O nosso complemento é para dados vetoriais, selecione Vector. Verifique a caixa Flag the plugin as experimental na parte inferior. Clique em OK`.

../_images/213.png
  1. Em seguida, você será solicitado a escolher um diretório para o seu plugin. Você precisa navegar para a pasta de complementos python do QGIS no seu computador e selecionar Select Folder. Normalmente, o diretório .qgis2/ está localizado em seu diretório pessoal. O local da pasta plugin vai depender de sua plataforma como segue: (Substitua username com o seu nome de login)

Windows

c:\Users\username\.qgis2\python\plugins

Mac

/Users/username/.qgis2/python/plugins

Linux

/home/username/.qgis2/python/plugins
../_images/37.png
  1. Você verá um diálogo de confirmação uma vez que seu modelo de complemento for criado. Observe o caminho para a pasta do complemento.

../_images/44.png
  1. Antes de podermos usar o complemento recém-criado, é preciso compilar o arquivo resources.qrc que foi criado pelo Plugin Builder. Inicie o :guilabel: OSGeo4W Shell em janelas ou em um terminal no Mac ou Linux.

../_images/54.png
  1. Navegue até a pasta de complementos em que a saída do Plugin Builder foi criada. Você pode usar o comando cd seguido do caminho para o diretório.

cd c:\Users\username\.qgis2\python\plugins\SaveAttributes
../_images/64.png
  1. Uma vez que você está no diretório, tipo `` make``. Isto irá executar o comando `` pyrcc4`` que tínhamos instalado como parte de ligações Qt para Python.

make
../_images/74.png
  1. Agora estamos prontos para ter um primeiro olhar para o novo complemento que criamos. Feche o QGIS e abra-o novamente. vá para Plugins ‣ Gerenciar e instalar complementos e permita que o complemento Salvar Attributes no guia Installed. Você vai notar que há um novo ícone na barra de ferramentas e uma nova entrada de menu em Vector ‣ Save Atributos -> Save Atributos como CSV`. Selecione-o para iniciar o diálogo do complemento.

../_images/84.png
  1. Você vai notar uma nova caixa de diálogo em branco chamada Save Attributes. Feche este diálogo.

../_images/94.png
  1. Agora vamos projetar sua caixa de diálogo e adicionar alguns elementos da interface do usuário para ele. Abra o programa Qt Creator e File –> Open File or Project....

../_images/104.png
  1. Navegue até o diretório do complemento e selecione o arquivo save_attributes_dialog_base.ui. Clique em Open.

../_images/117.png
  1. Você verá a caixa de diálogo em branco do plugin. Você pode arrastar e soltar elementos do painel do lado esquerdo da caixa de diálogo. Nós vamos adicionar um Combo Box do tipo Input Widget. Arraste-o para a janela do complemento.

../_images/124.png
  1. Redimensionar a caixa de combinação e ajuste seu tamanho. Agora arraste Display Widget no diálogo.

../_images/134.png
  1. Clique no texto do rótulo e digite Select a layer.

../_images/144.png
  1. Salve este arquivo, vá para File ‣ Save save_attributes_dialog_base.ui. Observe que o nome do objeto da caixa de combinação é comboBox. Para interagir com este objeto usando o código python, teremos que se referir a ele por este nome.

../_images/154.png
  1. Vamos recarregar nosso complemento para que possamos ver as mudanças na janela de diálogo. vá para Plugin ‣ Plugin Reloader ‣ Choose a plugin to be reloaded.

../_images/164.png
  1. Selecione SaveAttributes no`diálogo Configure Plugin reloader.

../_images/173.png
  1. Agora clique no botão Save Attributes as CSV. Você verá a caixa de diálogo recém-projetada.

../_images/184.png
  1. Vamos adicionar alguma lógica para o complemento que irá preencher a caixa de combinação com as camadas carregadas no QGIS. Vá para o diretório do complemento e carregue o arquivo save_attributes.py em um editor de texto. Role para baixo e encontre ométodo run(self). Este método será chamado quando você clica no botão da barra de ferramentas ou seleciona o item de menu do complemento. Adicione o seguinte código no início do método. Este código obtém as camadas carregadas no QGIS e as adiciona para o objeto comboBox a partir do diálogo plugin.

layers = self.iface.legendInterface().layers()
layer_list = []
for layer in layers:
     layer_list.append(layer.name())
     self.dlg.comboBox.addItems(layer_list)
../_images/192.png
  1. Volte na janela principal do QGIS, recarregue o complemento, vá para Plugins ‣ Plugin Reloader ‣ Reload plugin: SaveAttributes. Alternativamente, você pode pressionar F5. Para testar essa nova funcionalidade, é preciso carregar algumas camadas no QGIS. Depois de carregar alguns dados, inicie o plugin, vá para Vector ‣ Save Attributes ‣ Save Attributes as CSV.

../_images/201.png
  1. Você vai ver que a nossa caixa de combinação é agora preenchida com os nomes das camadas que estão carregadas no QGIS.

../_images/214.png
  1. Vamos adicionar outros elementos de interface de usuário restantes. Alterne de volta para o Qt Creator e carregue o arquivo save_attributes_dialog_base.ui. Adicione um Label Display Widget e altere o texto para Select output file. Adicionar um LineEdit tipo Input Widget que irá mostrar o caminho do arquivo de saída que o usuário escolheu. Em seguida, adicione um Push Button tipo Button e mude o rótulo do botão para .... Observe os nomes de objeto dos widgets que teremos de usar para interagir com eles. Salve o arquivo.

../_images/221.png
  1. Vamos agora adicionar o código python para abrir um navegador de arquivos quando o usuário clicar no botão ... e mostrar o caminho selecionado no widget. Abra o arquivo save_attributes.py em um editor de texto. Adicione QFileDialog à nossa lista de importações no topo do arquivo.

../_images/231.png
  1. Adicione um novo método chamado select_output_file com o seguinte código. Este código irá abrir um navegador de arquivos e preencher a linha de edição com o caminho do arquivo que o usuário escolher.

def select_output_file(self):
    filename = QFileDialog.getSaveFileName(self.dlg, "Select output file ","", '*.txt')
    self.dlg.lineEdit.setText(filename)
../_images/241.png
  1. Agora precisamos adicionar o código para que quando o botão ... for clicado, o método select_output_file seja chamado. Role até o método __init__ e adicione as seguintes linhas na parte inferior. Este código irá limpar o texto carregado anteriormente (se houver) na linha de edição e também conectar o método select_output_file ao sinal clicked do widget de botão.

self.dlg.lineEdit.clear()
self.dlg.pushButton.clicked.connect(self.select_output_file)
../_images/251.png
  1. Volte no QGIS, recarregue o plugin e abra odiálogo Save Attributes`. Se tudo correu bem, você será capaz de clicar no botão ... e selecionar um arquivo de texto de saída do seu disco.

../_images/261.png
  1. Quando você clica em OK no diálogo do complemento, nada acontece. Isso é porque nós não adicionamos a lógica para puxar informações de atributos da camada e escrevê-lo para o arquivo de texto. Temos agora todas as peças no lugar para fazer exatamente isso. Encontre o lugar no método run onde se diz pass. Substitua-o com o código abaixo. A explicação para este código pode ser encontrada em Começando a Programar com o Phyton.

filename = self.dlg.lineEdit.text()
output_file = open(filename, 'w')

selectedLayerIndex = self.dlg.comboBox.currentIndex()
selectedLayer = layers[selectedLayerIndex]
fields = selectedLayer.pendingFields()
fieldnames = [field.name() for field in fields]

for f in selectedLayer.getFeatures():
    line = ','.join(unicode(f[x]) for x in fieldnames) + '\n'
    unicode_line = line.encode('utf-8')
    output_file.write(unicode_line)
output_file.close()
../_images/271.png
  1. Agora nosso complementoestá pronto. Recarregue o complemenbto e experimente-lo. Você vai descobrir que o arquivo de texto de saída que você escolheu terão os atributos da camada vetorial. Você pode fechar o diretório do complemento e compartilhá-lo com seus usuários. Eles podem descompactar o conteúdo do seu complemento e experimentar o seu complemento. Se este foi um verdadeiro complemento, você iria enviá-lo para o QGIS Plugin Repository de modo que todos os usuários do QGIS serão capazes de encontrar e fazer o download do plugin.

Nota

Este complemento é apenas para demonstração. Não publique este complemento ou envie-o para o repositório QGIS.

Abaixo está o arquivo save_attributes.py para tê-lo como uma referência.

# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin saves the attribute of the selected vector layer as a CSV file.
                              -------------------
        begin                : 2015-04-20
        git sha              : $Format:%H$
        copyright            : (C) 2015 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon, QFileDialog
# Initialize Qt resources from file resources.py
import resources_rc
# Import the code for the dialog
from save_attributes_dialog import SaveAttributesDialog
import os.path


class SaveAttributes:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'SaveAttributes_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = SaveAttributesDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Save Attributes')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'SaveAttributes')
        self.toolbar.setObjectName(u'SaveAttributes')
        
        self.dlg.lineEdit.clear()
        self.dlg.pushButton.clicked.connect(self.select_output_file)
        

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('SaveAttributes', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/SaveAttributes/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Save Attributes as CSV'),
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginVectorMenu(
                self.tr(u'&Save Attributes'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def select_output_file(self):
        filename = QFileDialog.getSaveFileName(self.dlg, "Select output file ","", '*.txt')
        self.dlg.lineEdit.setText(filename)
        
    def run(self):
        """Run method that performs all the real work"""
        layers = self.iface.legendInterface().layers()
        layer_list = []
        for layer in layers:
                layer_list.append(layer.name())
            
        self.dlg.comboBox.clear()
        self.dlg.comboBox.addItems(layer_list)
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            filename = self.dlg.lineEdit.text()
            output_file = open(filename, 'w')
           
            selectedLayerIndex = self.dlg.comboBox.currentIndex()
            selectedLayer = layers[selectedLayerIndex]
            fields = selectedLayer.pendingFields()
            fieldnames = [field.name() for field in fields]
            
            for f in selectedLayer.getFeatures():
                line = ','.join(unicode(f[x]) for x in fieldnames) + '\n'
                unicode_line = line.encode('utf-8')
                output_file.write(unicode_line)
            output_file.close()
comments powered by Disqus

This work is licensed under a Creative Commons Attribution 4.0 International License