Python
PyQt
QGIS

QGIS3でpythonプラグインを作ってみた その3 QComboBoxとレイヤ取得について

QGIS3でpythonプラグインを作る その3

目標

前回作った地物カウントプラグインを、プラグイン上のコンボボックスで選択したレイヤに対して行うようカスタマイズする。

押さえたいところは

  • コンボボックスの扱い方
  • レイヤ一覧の取得方法

下準備

  • 2レイヤ以上登録されたQGISプロジェクトを用意
  • 前回作ったプラグインを有効にしておく
  • QGISのPluginReloaderプラグインを有効にしておく(任意)

コンボボックスを配置

self.cmb = QtWidgets.QComboBox(self)
self.cmb.move(30, 20)

アイテムの追加は変わりないですね。
表示はレイヤ名で値はレイヤIDにしないといけません。

self.cmb.addItem('')
self.cmb.addItem('a', 'a_123')
self.cmb.addItem('b', 'b_234')

選択変更イベントはこんな感じ

self.dlg.cmb.activated.connect(self.comboActivated) 

受け側は

def comboActivated(self, index):
    if index > 0:
        layer_id = self.dlg.cmb.itemData(index)

レイヤを取得

今回はベクタレイヤだけを対象としたレイヤリストを作ります。
QGIS2では以下のように取得していました。

vector_layers = []
layermap = QgsMapLayerRegistry.instance().mapLayers()
for id, layer in layermap.iteritems():
    if layer.type() == QgsMapLayer.VectorLayer:
        vector_layers.append(layer.name())

QGIS3ではlegendInterfaceQgsMapLayerRegistryは無くなりましたので
代わりにQgsProjectを使って取得します。
https://qgis.org/api/classQgsProject.html
またPython3ではディクショナリの.iteritems()が無くなりましたので.items()を使って回します。

vector_layers = []
layermap = QgsProject.instance().mapLayers()
for key, layer in layermap.items():
    if layer.type() == QgsMapLayer.VectorLayer:
        vector_layers.append(layer.name())

レイヤIDでレイヤを取得するには上と同様にQgsMapLayerRegistryQgsProjectに変更するだけで大丈夫ですね。

QgsMapLayerRegistry.instance().mapLayer(id)

 ↓

QgsProject.instance().mapLayer(id)

ソースに反映する

画面操作を関数にまとめました。
前回挿入したself.dlg.btn1.clicked.connect(self.buttonClicked)は削除して以下のように編集します。

test_plugin.py
...
        # Create the dialog (after translation) and keep reference
        self.dlg = TestPluginDialog(self.iface.mainWindow())
        self.first_flg = True
...
    def run(self):
        """Run method that performs all the real work"""
        self.dlgUpdate()
        # show the dialog
        self.dlg.show()
...
    def dlgUpdate(self):
        if self.first_flg:
            self.dlg.btn1.clicked.connect(self.buttonClicked)
            self.dlg.cmb.activated.connect(self.comboActivated)
            self.first_flg = False
        self.setCmb()
...
    def setCmb(self):
        self.dlg.cmb.clear()
        layermap = QgsProject.instance().mapLayers()
        self.dlg.cmb.addItem('')
        for key, layer in layermap.items():
            if layer.type() == QgsMapLayer.VectorLayer:
                self.dlg.cmb.addItem(layer.name(), key)

    def comboActivated(self, index):
        if index > 0:
            layer_id = self.dlg.cmb.itemData(index)
            layer = QgsProject.instance().mapLayer(layer_id)

            if layer is not None and layer.type() == QgsMapLayer.VectorLayer:
                cnt = layer.featureCount()
                QMessageBox.information(self.dlg,
                                        'count', str(cnt))

test_plugin_dialog.py
    def initUI(self):
      self.btn1 = QtWidgets.QPushButton("Button", self)
      self.btn1.move(30, 50)
      self.cmb = QtWidgets.QComboBox(self)
      self.cmb.move(30, 20)

動作確認

プラグインを再読み込みして起動します。
コンボボックスを選ぶと地物数が表示されました。
test_plugin_3.png

目的達成です。

でもQGISを再起動してプロジェクト読んですぐにプラグイン起動するとレイヤ一覧がコンボにセットされませんね。:tired_face:
プラグインをリロードするとセットされるようになりますが原因が判りません。
すっきり終わらないの困るなぁ。

追記(2018/3/2):
画面操作関数の呼び出し場所をinitからrunに移動して解決しました。
記事にも修正内容を反映しています。

追記(2018/3/3):
プラグイン呼び出しをするたびにイベントが追加されてしまっていましたので修正しました。

まとめ

コンボボックスの使い方は変わらない。
コネクトの仕方は前回のボタンと変わりませんね。
引数指定はしなくてもちゃんと飛ぶみたいです。

# 選択インデックスの場合
self.dlg.cmb.activated.connect(self.comboActivated) 
# 選択文字列の場合
self.dlg.cmb.activated[str].connect(self.comboActivated) 

レイヤ一覧の取得は

QgsProject.instance().mapLayers()

レイヤIDでレイヤを取得するには

QgsProject.instance().mapLayer(id)

ディクショナリを回すときは.iteritems()から.items()に変更


この記事のプラグインをGitHubに公開しました。
https://github.com/ozo360/TestPlugin/tree/part3

次回予告

次回は地物の操作をしてみたいと思います。

関連記事

QGIS3でpythonプラグインを作ってみた その1 ベース作成
QGIS3でpythonプラグインを作ってみた その2 QButtonと選択レイヤ取得について
QGIS3でpythonプラグインを作ってみた その3 QComboBoxとレイヤ取得について
QGIS3でpythonプラグインを作ってみた その4 地物の追加編集削除について
QGIS3でpythonプラグインを作ってみた その5 地図をクリックして地物を選択する

本記事のライセンス

クリエイティブ・コモンズ・ライセンス
この記事は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。