Zásuvné moduly, tzv. pluginy představují doplňkové nástroje. Jejich úlohou je rozšiřovat funkčnost a širokou škálu použití prostředí QGIS. Úvod do této problematiky je součástí školení QGIS pro začátečníky.
V mnohých případech však může nastat situace, kdy žádný z existujících zásuvných modulů nesplňuje funkcionalitu jakou bychom právě potřebovali. Úroveň rozšiřovaní funkcionality QGIS je různorodá. Za pomoci jazyka Python může jít o přidání jednoduchého tlačítka až po tvorbu sofistikovaných nástrojů. Kromě jazyka Python lze psát zásuvné moduly i v jazyku, ve kterém je napsán samotný QGIS, a to C++. To ale není úplně běžné.
V následující části načrtneme návod, jak si vlastní plugin vytvořit. Použijeme výše uvedený programovací jazyk Python. Postup následně odzkoušíme na jednoduchém reálném příkladu. Vytvoříme zásuvný modul s názvem Save Views, který exportuje grafický výstup ve formě obrázků ve formátu PNG pro každý prvek ve vybrané vektorové vrstvě do zvoleného výstupního adresáře.
Užitečné odkazy
- PyQGIS Developer Cookbook
- QGIS API (originální pro C++)
- QGIS Python API (přepis pro jazyk Python)
Důležité
QGIS verze 2 a 3 se významně liší, a to nejen z uživatelského pohledu, ale i tvorby pluginů. QGIS API verze 3 není s předcházející verzí zpětně kompatibilní. Obsahuje řadu změn, viz Backwards Incompatible Changes. Tento návod je psán pro verzi QGIS 3.
Potřebné nástroje¶
I. Qt Designer¶
Pro tvorbu nového pluginu budeme potřebovat Qt Designer, což je aplikace vývojového frameworku Qt. Tento nástroj využijeme pro tvorbu uživatelského rozhraní nového modulu.
Poznámka
Způsob instalace Qt Designer se liší podle platformy. Na Windows je Qt Designer součástí instalace samotného QGISu. Doporučený postup je použít instalátor OSGeo4W, vybrat volbu Express Desktop a nainstalovat balík QGIS. Qt Designer spustíme přes volbu Qt Designer with QGIS custom widgets. V případě Ubuntu/Debian je Qt Designer součástí balíčku qttools5-dev-tools.
II. Python rozhraní pro Qt¶
Vzhledem k tomu, že budeme vyvíjet plugin v programovacím jazyku
Python, musíme nainstalovat Python rozhraní (Python
bindings) pro Qt (verze 5). Pro
tvorbu zásuvných modulů je potřeba také nástroj pyrcc5
.
Poznámka
Na Windows obsahuje instalátor OSGeo4W vše potřebné. Po
instalaci je nástroj pyrcc5
dostupný v rámci OSGeo4W Shell.
Na Mac OS je potřeba nainstalovat správce balíčků Homebrew a doinstalovat balíček PyQt. V případě Linuxu
(Ubuntu/Debian) jde o balíček pyqt5-dev-tools. V Ubuntu jej
např. nainstalujeme příkazem sudo apt-get install
pyqt5-dev-tools
.
III. Textový editor¶
Vhodný textový editor anebo integrované vývojové prostředí (IDE) jsou pro psaní zdrojového kódu důležité. Mezi oblíbené editory patří například Sublime Text, Vim, GNU Emacs, Notepad++, TextWrangler, IDLE, Atom, Aquamacs, GNU Nano, Kate, gedit, prostředí Spyder či PyCharm a podobně.
Poznámka
V našem případě si vystačíme s vestavěným Python editorem v QGISu. Pro dlouhodnou práci tento editor ale vhodný není.
IV. Zásuvný modul Plugin Builder¶
Tento velmi užitečný zásuvný modul QGISu nám pomůže vytvořit všechny potřebné soubory a standardní podobu kódu pro budoucí plugin. Nainstalujeme jej klasickým způsobem pomocí správce zásuvných modulů v QGISu, viz. školení QGIS pro začátečníky.
V. Zásuvný modul Plugin Reloader¶
Díky tomuto pluginu nemusíme při každé změně našeho kódu restartovat QGIS. Změny se projeví ihned po jeho spuštění. Nainstalujeme jej klasickým způsobem pomocí správce zásuvných modulů v QGISu (pro jeho instalaci je potřeba povolit experimentální moduly v nastavení správce).
Pět základních kroků pro vytvoření pluginu Save Views¶
1. Vytvoření šablony nového pluginu pomocí zásuvného modulu Plugin Builder
2. Překlad qrc souboru
3. Načtení nového pluginu ve správci zásuvných modulů
4. Vytvoření uživatelského rozhraní pomocí Qt Designer
5. Implementace funkcionality pluginu v rámci Python kódu
1. Vytvoření šablony nového pluginu¶
Po spuštění zásuvného modulu pro tvorbu pluginů se objeví dialogové okno, kde zadáme základní údaje o našem novém nástroji, viz. Obr. 3.
Poznámka
Class name je název Python třídy a Module name název adresáře. U těchto položek nepoužívejte diakritiku, mezery a pod.
V třetí části vybereme šablonu uživatelského prostředí. K dispozici jsou tři:
- Tool button with dialog (samostatné okno)
- Tool button with dockwidget (přichytnutelné okno)
- Processing provider (poskytovatel pro nástoje zpracování, viz školení QGIS pro pokročilé)
Zadáme text, který se bude zobrazovat v menu. Nakonec vybereme, pod kterou položkou v menu náš nový plugin uživatel najde.
Následně se objeví okno, kde je potřebné zadat cestu, ve kterém se
adresář s vytvořeným pluginem vytvoří. Zvolíme vhodné umístění na
vašem disku, v našem případě /opt/qgis_plugins
.
Po tomto kroku se objeví potvrzující dialog, tzv. Plugin Builder Results obsahující souhrnné informace.
Na tomto místě najdete jednu podstatnou informaci, a to cestu k
adresáři, kde vaše instalace QGISu hledá při načítání zásuvné
moduly. Tato cesta je závislá na platfomě. Pod Linuxem je to typicky
$HOME/.local/share/QGIS/QGIS3/profiles/default/python/plugins
. Pod
Windows
%APPDATA%\Roaming\QGIS\QGIS3\profiles\default\python\plugins
.
2. Překlad qrc souboru¶
Důležité
Tento krok provede Plugin Builder automaticky, pokud je
v systémové cestě dostupný nástroj pyrcc5
.
Otevřte adresář s vytvořeným pluginem, v našem případě
/opt/qgis_plugins/save_views
. Pokud se soubor
resources.py
nevytvořil, je potřeba to napravit. Spustíme z
příkazové konzole příkaz make
. Ten vytvoří na základě Resource
Collection File (*.qrc) jeho Python přepis. Jde vlastně o spuštění
výše uvedeného pyrcc5
.
3. Načtení nového pluginu ve správci zásuvných modulů¶
K tomu, aby nový plugin QGIS po startu našel, existuje více
možností. Nejjednoduší variantou je adresář s pluginem překopírovat do
výchozí cesty zásuvných modulů, viz Krok 1. Lepší
variantou je definovat v nastavení QGISu ( ) proměnnou prostředí QGIS_PLUGINPATH
ukazující na nadřazený adresář vašeho pluginu,
tj. /opt/qgis_plugins
. Po restartu bude QGIS zobrazovat
všechny pluginy, které do tohoto adresáře v budnoucnu umístíte.
Po opětovném startu QGISu by měl být v sekci se objeví jeho ikona a nová položka v hlavním menu, tak jako jsme zadali, tj. v sekci .
viditelný i plugin Save Views. ZaškrtnutímSpuštěním otevřeme dialog nástroje, který obsahuje popisek (Obr. 12).
Tip
V této fázi je dobré se zamyslet nad funkcionalitou pluginu, jaký bude typ vstupních dat a podobně. Na základě našich požadavků je vhodné si vytvořit testovací sadu.
Vytvoříme si jednoduchý projekt v QGISu, který bude obsahovat několik vektorových vrstev. Na Obr. 13 jsou zobrazeny například požární stanice, železnice, kraje, velkoplošné území a státní hranice České republiky.
4. Vytvoření uživatelského rozhraní pomocí Qt Designer¶
Vzhled a elementy dialogového okna pluginu budeme upravovat v programu Qt Designer. V hlavní liště zvolíme Obr. 14 je znázorněné prozatím prázdné okno (SaveViewsDockWidgetBase) s objekty (tzv. widgety) dockWidgetContets a label. Pomocí metody drag-and-drop je možné z levého panelu přidávat další objekty a jejich názvy a vlastnosti měnit v pravé části okna aplikace Qt Designer.
a otevřeme soubor s příponou *.ui. V našem případě save_views_dockwidget_base.ui, který najdeme v adresáři vytvořeného pluginu. NaNejrpve v pravém panelu změníme předvolený text objektu label na
Select a layer
(Obr. 15).
Dále v levé části okna najdeme objekt typu Combo Box a tažením myši jej umistíme do vhodného místa okna zásuvného modulu.
Tip
Pro reálný vývoj je vhodné výchozí názvy objektů (v našem případě label a comboBox) nastavit na hodnoty odpovídajícím reálnému využití, např.místo combobox layerSelect a pod.
Po uložení |plugin-reloader|. V Choose a plugin to be reloaded nastavíme SaveViews (Obr. 17) a plugin spustíme. Tím se aktualizuje podoba našeho pluginu.
přejdeme do prostředí QGIS, kde použijeme plugin Plugin ReloaderPo kliknutí na ikonu se otevře okno odpovídající návrhu na Obr. 15.
5. Implementace funkcionality nástroje a další úpravy¶
Řekněme, že chceme, aby se po spuštění pluginu Combo Box automaticky
naplnil vektorovými vrstvami aktuálního projektu. Hlavním souborem,
který se stará o logiku jednotlivých objektů, je v našem případě
save_views.py
. Otevřeme jej v textovém editoru a najdeme
metodu run()
. Tato metoda se spouští při každém startu pluginu. Na
její konec umístíme následující kód (Obr. 19).
# populate the Combo Box with the vector layers loaded in QGIS
from qgis.core import QgsProject, QgsMapLayer
self.dockwidget.comboBox.clear()
for layer in QgsProject.instance().mapLayers().values():
if layer.type() != QgsMapLayer.VectorLayer:
continue
self.dockwidget.comboBox.addItem(layer.name())
Po znovu načtení pluginu |plugin-reloader| a jeho otevření je vidět, že se změny úspěšně projevily (Obr. 20).
Poznámka
V případě, že skončí spustění či znovunačtení chybou, tak hledejte relevatní informace ve Zprávách výpisů, konkrétně v záložce Python chyba.
Tip
Namísto obecného objektu Combo Box by bylo možné použít
specifický widget QGISu, a to QgsMapLayerComboBox
. Využití
tohoto specifického widgetu by nám ušetřilo pár řádek kódu.
Todo
doplnit nazev tridy
Úkol
Seznam vrstev se načítá pouze při spuštění pluginu. Upravte zdrojový kód tak, aby umožňoval znovunačtení seznamu vrstev i během běhu pluginu.
Obdobně vložíme do okna další elementy a přiřadíme jim příslušnou
funkcionalitu. Kromě popisu Select output directory
půjde o
objekty Line Edit, Tool Button, Push Button. U widgetu
pushButton ve vlastnostech změníme text na Save All
. Nastavíme
přiměřené rozměry pro každý element a upravený soubor uložíme.
Důležité jsou názvy jednotlivých objektů, viz. Obr. 21,
budeme je ještě potřebovat.
Poznámka
Pokud je uživatelské rozhraní definováno více objekty (widgety) je vhodné je rozumně pojmenovat. V případě našeho jednoduchého modulu si vystačíme s předvolenými názvy.
V dalším kroku potřebujeme přidat kód, který zabezpečí, aby se po
kliknutí na tlačítko ...
otevřel dialog, ve kterém zvolíme adresář
pro uložení výsledných obrazových souborů pro každý prvek ve vybrané
vektorové vrstvě. O tuto funkcionalitu se postará nová metoda
select_output_directory()
, kterou přidáme na konec souboru
save_views.py
, Obr. 22.
# open directory browser and populate the line edit widget
def select_output_dir(self):
from PyQt5.QtWidgets import QFileDialog
self.dirname = QFileDialog.getExistingDirectory(
self.dockwidget, "Select directory ", os.path.expanduser("~")
)
self.dockwidget.lineEdit.setText(self.dirname)
Tip
os.path.expanduser("~")
nastaví cestu při otevření
dialogu na domovský adresář.
Poznámka
Kód importující použité třídy jako např. from
PyQt5.Widgets import QFileDialog
je vhodnější umístit na začátek
souboru. Zde uvádíme především pro přehlednost úprav.
Úkol
Upravte zdrojový kód tak, aby si dialog pamatoval poslední použitý adresář.
Následně propojíme metodu select_output_dir()
s tlačítkem
toolButton
(tlačítko ...
). To provedeme přidáním níže
uvedených řádků do metody __run__()
, nejlépe na konec těla
podmínky if self.dockwidget == None
, viz
Obr. 22.
# connect the select_output_file() method to the clicked signal of the tool button widget
self.dockwidget.toolButton.clicked.connect(self.select_output_dir)
Na konec metody __run__()
ještě přidáme kód zajišťující obnovení
prázdného obsahu objektu lineEdit
.
# clear the previously loaded text (if any) in the line edit widget
self.dockwidget.lineEdit.clear()
Soubor uložíme, plugin znovu načteme a vyzkoušíme (Obr. 24).
Posledním krokem je změnit to, aby se po kliknutí na tlačítko Save
all
opravdu provedlo, co chceme. Vytvoříme novou metodu
save_views()
, kterou umístíme na konec souboru
save_views.py
, viz Obr. 25.
def save_views(self):
from PyQt5.QtGui import QColor, QPixmap
from qgis.core import QgsProject
from qgis.utils import iface
# save graphical output for every row in attribute table
layer_name = self.dockwidget.comboBox.currentText()
layer = QgsProject.instance().mapLayersByName(layer_name)[0]
for feature in layer.getFeatures():
layer.selectByIds([feature.id()])
self.iface.mapCanvas().setSelectionColor(QColor("transparent"));
box = layer.boundingBoxOfSelected()
self.iface.mapCanvas().setExtent(box)
pixmap = QPixmap(self.iface.mapCanvas().mapSettings().outputSize().width(),
self.iface.mapCanvas().mapSettings().outputSize().height()
)
mapfile = os.path.join(self.dirname, '{0}_{1:03d}.png'.format(layer_name, feature.id()))
self.iface.mapCanvas().saveAsImage(mapfile, pixmap)
layer.removeSelection()
# save also full extend of vector layer
canvas = self.iface.mapCanvas()
canvas.setExtent(layer.extent())
pixmap = QPixmap(self.iface.mapCanvas().mapSettings().outputSize().width(),
self.iface.mapCanvas().mapSettings().outputSize().height()
)
mapfile = os.path.join(self.dirname, '{}_full.png'.format(layer_name))
self.iface.mapCanvas().saveAsImage(mapfile, pixmap)
Tuto metodu provážeme s tlačítkem Save all
.
# connect the save_views() method to the clicked signal of the push button widget
self.dockwidget.pushButton.clicked.connect(self.save_views)
Úkol
Opravte chybu, která nastane po stisknutí tlačítka Save
all
v případě, že není nastaven adresář pro výstupní soubory.
Úkol
Upravte kód tak, aby mohl zadat uživatel výstupní adresář
ručně bez tlačítka ...
.
Grafické výstupy po aplikovaní na vrstvu krajů jsou zobrazeny na Obr. 26. Jejich názvy v adresáři závisí na názvu konkrétní vektorové vrstvy. Liší se pouze pořadovým číslem.
Tip
V případě, že chceme změnit ikonu, stačí nový soubor s ikonkou,
např. Obr. 27, uložit do adresáře save_views
jako soubor icon.png
a spustit příkaz make clean && make
v
příkazové řádce. Nakonec znovunačteme plugin pomocí modulu Plugin
Reloader.
Výsledný soubor save_views.py
je ke stažení také zde.
Jiný příklad využití¶
Na obrázku Obr. 28 je uveden projekt s katastrálními daty. Vyznačené jsou parcely, přes které budou procházet plánované inženýrské sítě.
Použitím pluginu Save Views můžeme pro každého vlastníka vyhodnotit grafické znázornění jeho parcely, na které bude zapsané věcné břemeno (Obr. 29).