13. ユーザーとのコミュニケーション

ヒント

The code snippets on this page need the following imports if you're outside the pyqgis console:

 1from qgis.core import (
 2    QgsMessageLog,
 3    QgsGeometry,
 4)
 5
 6from qgis.gui import (
 7    QgsMessageBar,
 8)
 9
10from qgis.PyQt.QtWidgets import (
11    QSizePolicy,
12    QPushButton,
13    QDialog,
14    QGridLayout,
15    QDialogButtonBox,
16)

このセクションでは、ユーザーインターフェイスにおいて一貫性を維持するためにユーザーとのコミュニケーション時に使うべき方法と要素をいくつか示します。

13.1. Showing messages. The QgsMessageBar class

メッセージボックスを使用するのはユーザー体験の見地からは良いアイデアではありません。警告/エラー用に小さな情報行を表示するには、たいていQGIS メッセージバーが良い選択肢です。

QGIS インターフェイスオブジェクトへの参照を利用すると、次のようなコードでメッセージバー内にメッセージを表示できます。

from qgis.core import Qgis
iface.messageBar().pushMessage("Error", "I'm sorry Dave, I'm afraid I can't do that", level=Qgis.Critical)
Messages(2): Error : I'm sorry Dave, I'm afraid I can't do that
../../_images/errorbar.png

図 13.33 QGIS メッセージバー

表示期間を設定して時間を限定することができます。

iface.messageBar().pushMessage("Ooops", "The plugin is not working as it should", level=Qgis.Critical, duration=3)
Messages(2): Ooops : The plugin is not working as it should
../../_images/errorbar-timed.png

図 13.34 タイマー付きQGIS メッセージバー

The examples above show an error bar, but the level parameter can be used to creating warning messages or info messages, using the Qgis.MessageLevel enumeration. You can use up to 4 different levels:

  1. Info

  2. Warning

  3. Critical

  4. Success

../../_images/infobar.png

図 13.35 QGIS メッセージバー (お知らせ)

ウィジェットは、例えば詳細情報の表示用ボタンのように、メッセージバーに追加することができます

1def showError():
2    pass
3
4widget = iface.messageBar().createMessage("Missing Layers", "Show Me")
5button = QPushButton(widget)
6button.setText("Show Me")
7button.pressed.connect(showError)
8widget.layout().addWidget(button)
9iface.messageBar().pushWidget(widget, Qgis.Warning)
Messages(1): Missing Layers : Show Me
../../_images/bar-button.png

図 13.36 ボタン付きのQGIS メッセージバー

メッセージバーは自分のダイアログの中でも使えるため、メッセージボックスを表示する必要はありませんし、メインのQGISウィンドウ内に表示する意味がない時にも使えます。

 1class MyDialog(QDialog):
 2    def __init__(self):
 3        QDialog.__init__(self)
 4        self.bar = QgsMessageBar()
 5        self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
 6        self.setLayout(QGridLayout())
 7        self.layout().setContentsMargins(0, 0, 0, 0)
 8        self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok)
 9        self.buttonbox.accepted.connect(self.run)
10        self.layout().addWidget(self.buttonbox, 0, 0, 2, 1)
11        self.layout().addWidget(self.bar, 0, 0, 1, 1)
12    def run(self):
13        self.bar.pushMessage("Hello", "World", level=Qgis.Info)
14
15myDlg = MyDialog()
16myDlg.show()
../../_images/dialog-with-bar.png

図 13.37 カスタムダイアログ内のQGIS メッセージバー

13.2. プロセスを表示する

プログレスバーはご覧のとおりウィジェットを受け入れるので、QGISメッセージバーに置くこともできます。コンソール内で試すことができる例はこちらです。

 1import time
 2from qgis.PyQt.QtWidgets import QProgressBar
 3from qgis.PyQt.QtCore import *
 4progressMessageBar = iface.messageBar().createMessage("Doing something boring...")
 5progress = QProgressBar()
 6progress.setMaximum(10)
 7progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
 8progressMessageBar.layout().addWidget(progress)
 9iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)
10
11for i in range(10):
12    time.sleep(1)
13    progress.setValue(i + 1)
14
15iface.messageBar().clearWidgets()
Messages(0): Doing something boring...

Also, you can use the built-in status bar to report progress, as in the next example:

 1vlayer = iface.activeLayer()
 2
 3count = vlayer.featureCount()
 4features = vlayer.getFeatures()
 5
 6for i, feature in enumerate(features):
 7    # do something time-consuming here
 8    print('.') # printing should give enough time to present the progress
 9
10    percent = i / float(count) * 100
11    # iface.mainWindow().statusBar().showMessage("Processed {} %".format(int(percent)))
12    iface.statusBarIface().showMessage("Processed {} %".format(int(percent)))
13
14iface.statusBarIface().clearMessage()

13.3. ログを作成する

There are three different types of logging available in QGIS to log and save all the information about the execution of your code. Each has its specific output location. Please consider to use the correct way of logging for your purpose:

  • QgsMessageLog is for messages to communicate issues to the user. The output of the QgsMessageLog is shown in the Log Messages Panel.

  • The python built in logging module is for debugging on the level of the QGIS Python API (PyQGIS). It is recommended for Python script developers that need to debug their python code, e.g. feature ids or geometries

  • QgsLogger is for messages for QGIS internal debugging / developers (i.e. you suspect something is triggered by some broken code). Messages are only visible with developer versions of QGIS.

Examples for the different logging types are shown in the following sections below.

警告

Use of the Python print statement is unsafe to do in any code which may be multithreaded and extremely slows down the algorithm. This includes expression functions, renderers, symbol layers and Processing algorithms (amongst others). In these cases you should always use the python logging module or thread safe classes (QgsLogger or QgsMessageLog) instead.

13.3.1. QgsMessageLog

# You can optionally pass a 'tag' and a 'level' parameters
QgsMessageLog.logMessage("Your plugin code has been executed correctly", 'MyPlugin', level=Qgis.Info)
QgsMessageLog.logMessage("Your plugin code might have some problems", level=Qgis.Warning)
QgsMessageLog.logMessage("Your plugin code has crashed!", level=Qgis.Critical)
MyPlugin(0): Your plugin code has been executed correctly
(1): Your plugin code might have some problems
(2): Your plugin code has crashed!

注釈

You can see the output of the QgsMessageLog in the ログメッセージパネル

13.3.2. The python built in logging module

1import logging
2formatter = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
3logfilename=r'c:\temp\example.log'
4logging.basicConfig(filename=logfilename, level=logging.DEBUG, format=formatter)
5logging.info("This logging info text goes into the file")
6logging.debug("This logging debug text goes into the file as well")

The basicConfig method configures the basic setup of the logging. In the above code the filename, logging level and the format are defined. The filename refers to where to write the logfile to, the logging level defines what levels to output and the format defines the format in which each message is output.

2020-10-08 13:14:42,998 - root - INFO - This logging text goes into the file
2020-10-08 13:14:42,998 - root - DEBUG - This logging debug text goes into the file as well

If you want to erase the log file every time you execute your script you can do something like:

if os.path.isfile(logfilename):
    with open(logfilename, 'w') as file:
        pass

Further resources on how to use the python logging facility are available at:

警告

Please note that without logging to a file by setting a filename the logging may be multithreaded which heavily slows down the output.