PyQGIS マウスイベントサンプル

QGISのマップ上でマウスのクリック、移動イベントを受け取るプラグインのサンプルを幾つかメモしておきます。尚、プログラムの大半はPlugin Builderを使って作成しました。


MapTool版

プラグインのアイコンをクリックすると、マウスカーソルが十字になり、マップをクリックしたり移動した時に、その位置の座標をPythonコンソールに表示するサンプルです。

使い方

f:id:Chiakikun:20200222105739p:plain

ZIPファイルからインストールすると、メニューにプラグインが追加されるので、実行します。実行するとマウスカーソルがマップウィンドウ上で「+」に変わります。

 

f:id:Chiakikun:20200222111544p:plain

②「Pythonコンソール」を表示した状態でマウスをマップウィンドウ上で動かしたりマウスボタンをクリックすると、その位置が表示されます。

コード

maptool_sample.py

class MapToolSample:

    def __init__(self, iface):

~中略~

        # キャンバスウィンドウ上でのマウスイベントの設定
        self.mouseEventSample = MouseEventSample(self.iface, self.canvas)

~中略~

    def execSample(self):
        if self.action.isChecked():
            self.previousMapTool = self.canvas.mapTool()
            self.canvas.setMapTool(self.mouseEventSample)
        else:
            self.canvas.unsetMapTool(self.mouseEventSample)
            self.canvas.setMapTool(self.previousMapTool)
            self.action.setChecked(False)

        
class MouseEventSample(qgis.gui.QgsMapTool):
    def __init__(self, iface, canvas):
        qgis.gui.QgsMapTool.__init__(self, canvas)

    def canvasMoveEvent(self, event):
        print('マウス移動:' + str(self.toMapCoordinates(event.pos())))

    def canvasPressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            print('左クリック!' + str(self.toMapCoordinates(event.pos())))

        if event.button() == QtCore.Qt.RightButton:
            print('右クリック!' + str(self.toMapCoordinates(event.pos())))

8行目 QgsMapToolを継承したクラスのインスタンスを生成しています。

12行~19行目 このプラグインのアイコンが押されたときに呼ばれます。アイコンがONになった時、現在有効なマップツールを退避して、MouseEventSampleをマップツールに登録しています。アイコンがOFFになった時、MouseEventSampleをマップツールから削除して、退避しておいたマップツールを登録しています。

22行目~ マウス移動、ボタンクリックをしたときにPythonコンソールに座標を表示するクラスを定義しています。

こちらにソースを置いておきました。


MapToolEmitPoint版

ダイアログの『実行』ボタンをクリックすると、マウスカーソルが十字になり、マップをクリックしたり移動した時に、その位置の座標をPythonコンソールに表示するサンプルです。

使い方 

f:id:Chiakikun:20200223112027p:plain

zipファイルをインストールするとメニューにプラグインが追加されるので実行します。するとダイアログが表示されます。

『実行』ボタンを押下すると、マップウィンドウ上でマウスカーソルが「+」になります。

 

f:id:Chiakikun:20200223112517p:plain

②ボタンが「実行中」の間は、マウスを動かしたりボタンをクリックすると、その場所の座標がPythonコンソールに表示されます。

コード

maptoolemitpoint_sample_dialog.py

class MapToolEmitPointSampleDialog(QtWidgets.QDialog, FORM_CLASS):

    def __init__(self, iface, parent=None):

~中略~

        # キャンバスウィンドウ上でのマウスイベントの設定
        self.mouseEventSample = qgis.gui.QgsMapToolEmitPoint(self.canvas)

~中略~

    def pushExec(self, checked):
        if checked == True:
            self.mouseEventSample.canvasClicked.connect(self.mouseClick)
            self.mouseEventSample.canvasMoveEvent = self.canvasMoveEvent        
            self.canvas.setMapTool(self.mouseEventSample)

~中略~

    def canvasMoveEvent(self, event):
        print('マウス移動:' + str(self.canvas.getCoordinateTransform().toMapCoordinates(event.pos())))

 
    def mouseClick(self, currentPos, clickedButton ):
        if clickedButton == QtCore.Qt.LeftButton: 
            print('左クリック!' + str(qgis.core.QgsPointXY(currentPos)))

        if clickedButton == QtCore.Qt.RightButton:
            print('右クリック!' + str(qgis.core.QgsPointXY(currentPos)))

8行目 QgsMapToolEmitPointのインスタンスを生成しています。

12行~16行目 ダイアログの『実行』ボタンが押下されたとき、マップをクリックしたときに実行されるメソッドと、マウスを移動させたときに実行されるメソッドをMapToolに登録しています。

20行目~ マウス移動、ボタンクリックをしたときにPythonコンソールに座標を表示するメソッドを定義しています。

こちらにソースを置いておきました。


Rubber Band サンプル

最後に、MapToolEmitPointとQgsRubberBandを使って、マップに地物を描画するサンプルを作成してみました。このプラグインは下のサイトを参考にしました。

gis.stackexchange.com

使い方 

f:id:Chiakikun:20200223155848p:plain

①使い方はMaptoolEmitPoint版のサンプルとほとんど変わりません。zipファイルをインストールするとメニューにプラグインが追加され、プラグインを実行するとダイアログが表示されます。

 マウスの左クリックで描画開始、描画中ならノード追加をします。マウスの右クリックで描画終了で仮想レイヤにレコードが追加されます。

コード

rubberbandSample_dialog.py

class RubberBandSampleDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):

~中略~

        # 仮想レイヤ作成
        self.vlyr = qgis.core.QgsVectorLayer("Polygon?&crs=epsg:4326&field=name:string&field=size:double", "サンプルレイヤ", "memory")
        # キャンバスウィンドウ上でのマウスイベントの設定
        self.mouseEventSample = qgis.gui.QgsMapToolEmitPoint(self.canvas)

~中略~

    def pushExec(self, checked):
        if checked == True:
            self.mouseEventSample.canvasClicked.connect(self.mouseClick)
            self.mouseEventSample.canvasMoveEvent = self.canvasMoveEvent        
            self.canvas.setMapTool(self.mouseEventSample)

            # このサンプル以外のアイコンを押した場合に、凹んでいる実行ボタンを元に戻すため
            self.canvas.mapToolSet.connect(self.unsetTool)
            self.pushButton_Exec.setText('実行中')

            self.myRubberBand = None
        else:
            self.unsetTool()


    def canvasMoveEvent(self, event):
        if self.myRubberBand == None: # 一度も左ボタンをクリックしてない時にこのメソッドが呼ばれた場合
            return

        # 地物の描画中に、マウスカーソルを追って形状が変わるように
        point = self.canvas.getCoordinateTransform().toMapCoordinates(event.pos())
        self.myRubberBand.movePoint(point)


    def mouseClick(self, currentPos, clickedButton ):
        # 地物の最初の一点目
        if clickedButton == QtCore.Qt.LeftButton and self.myRubberBand == None:
            self.myRubberBand = qgis.gui.QgsRubberBand( self.canvas, qgis.core.QgsWkbTypes.PolygonGeometry )
            self.myRubberBand.setColor( QColor(78, 97, 114, 190) )
            self.myRubberBand.addPoint( qgis.core.QgsPointXY(currentPos) )

        # 地物の二点目以降
        if clickedButton == QtCore.Qt.LeftButton and self.myRubberBand.numberOfVertices() > 0:
            self.myRubberBand.addPoint( qgis.core.QgsPointXY(currentPos) )

        # __init__で作成した仮想レイヤのレコード作成
        if clickedButton == QtCore.Qt.RightButton:
            # フィールド設定
            qf = qgis.core.QgsFields()
            for field in self.vlyr.fields():
                qf.append(qgis.core.QgsField(str(field.name()), typeName=field.typeName()))
            record = qgis.core.QgsFeature(qf) 

            # ラバーバンドで作成した地物をセットする
            geomP = self.myRubberBand.asGeometry() 
            record.setGeometry(geomP) 

            # 属性をセットする
            record.setAttributes([str(datetime.now()), geomP.area()])

            # 作成したレコードをレイヤに追加
            self.vlyr.dataProvider().addFeatures([record])
            self.vlyr.updateExtents() # これが無いと『レイヤの領域にズーム』した時に、レイヤの最初のオブジェクト部分しかズームされない

            # キャンバスにオブジェクトを表示する      
            qgis.core.QgsProject.instance().addMapLayers([self.vlyr])
            #self.canvas.setExtent(geomP.boundingBox()) #パンしたい場合は#外して
            self.canvas.refreshAllLayers()

            self.canvas.scene().removeItem(self.myRubberBand) # ラバーバンドで描いた図形はもう必要ないので消す
            self.myRubberBand = None

こちらにソースを置いておきました。


ラスタの値取得

MapTool版を少しいじって、マウスカーソル下のラスタのピクセル値を表示するサンプルを作成しました。

このプラグインは下のサイトを参考にしました。

gis.stackexchange.com

使い方

f:id:Chiakikun:20200223185047p:plain

①使い方はMapTool版のサンプルとほとんど変わりません。zipファイルをインストールすると、プラグインがメニューに追加されます。

プラグインを実行するとマウスカーソルが「+」に変わるので、アクティブにしたラスタレイヤの上にマウスカーソルを移動させると、カーソル下のピクセル値を表示します。

コード

getrastervalue_sample.py

    def __init__(self, iface):

~中略~

        # キャンバスウィンドウ上でのマウスイベントの設定
        self.mouseEventSample = MouseEventSample(self.iface, self.canvas)

~中略~

class MouseEventSample(qgis.gui.QgsMapTool):
    def __init__(self, iface, canvas):
        qgis.gui.QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.iface = iface

        # canvasMoveEventで設定した秒数(このプログラムでは700ms)経過したら呼ばれるメソッドを設定
        self.timerMapTips = QTimer( canvas )
        self.timerMapTips.timeout.connect( self.showMapTip )


    def canvasMoveEvent(self, event):
        QToolTip.hideText()
        self.timerMapTips.start( 700 )


    def showMapTip( self ):
        self.timerMapTips.stop()

        rLayer = self.iface.activeLayer()
        if type(rLayer) is qgis.core.QgsRasterLayer:
            # マウスカーソル下のピクセル値を取得する
            mappos = self.toMapCoordinates(self.canvas.mouseLastXY())
            ident = rLayer.dataProvider().identify( mappos, qgis.core.QgsRaster.IdentifyFormatValue ) # 参考: https://qgis.org/api/qgsraster_8h_source.html#l00057

            # ツールチップに表示する文字列を設定する
            if ident.isValid():
                values = ident.results().values()
                text = ", ".join(['{0:g}'.format(r) for r in values if r is not None] ) # 複数取得することは無いだろうけど一応
            else:
                text = "Non valid value"

            QToolTip.showText( self.canvas.mapToGlobal( self.canvas.mouseLastXY() ), text, self.canvas )

こちらにソースを置いておきました。



最後までご覧いただきありがとうございます。