13. 与用户通信

提示

如果您在pyqgis控制台之外,则此页面上的代码片段需要以下导入:

 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. 显示消息。QgsMessageBar类

从用户体验的角度来看,使用消息框可能不是一个好主意。对于显示小信息行或警告/错误消息,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.10 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.11 带计时器的QGIS消息栏

上面的示例显示了一个错误条,但 level 参数可用于创建警告消息或信息消息,使用 Qgis.MessageLevel 枚举。您最多可以使用4个不同的级别:

  1. 信息

  2. 警告

  3. 批判性

  4. 成功

../../_images/infobar.png

图 13.12 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.13 带有按钮的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.14 自定义对话框中的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...

此外,您还可以使用内置状态栏来报告进度,如下例所示:

 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. 日志记录

QGIS中有三种不同类型的日志记录,可用于记录和保存有关代码执行的所有信息。每个都有其特定的输出位置。请考虑为您的目的使用正确的日志记录方式:

  • QgsMessageLog 是让消息将问题传达给用户。QgsMessageLog的输出显示在日志消息面板中。

  • 内置的 Python logging 模块用于在QGIS Python API(PyQGIS)级别上进行调试。建议需要调试其Python代码(例如要素ID或几何)的Python脚本开发人员使用此工具

  • QgsLogger 用于以下对象的消息 QGIS internal 调试/开发人员(即您怀疑某些损坏的代码触发了某些内容)。消息仅在开发人员版本的QGIS中可见。

以下各节显示了不同日志记录类型的示例。

警告

Python 的使用 print 语句在任何可能是多线程的代码中执行都是不安全的 extremely slows down the algorithm 。这包括 expression functionsrendererssymbol layersProcessing algorithms (在其他方面)。在这些情况下,您应该始终使用 logging 模块或线程安全类 (QgsLoggerQgsMessageLog )相反。

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!

备注

您可以看到 QgsMessageLog日志消息面板

13.3.2. 内置的日志记录模块

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")

BasicConfig方法配置日志记录的基本设置。在上面的代码中,定义了文件名、日志级别和格式。文件名是指将日志文件写入的位置,日志记录级别定义了要输出的级别,格式定义了输出每条消息的格式。

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 os.path.isfile(logfilename):
    with open(logfilename, 'w') as file:
        pass

有关如何使用Python日志记录工具的更多资源,请访问:

警告

请注意,如果不通过设置文件名来记录到文件,则记录可能是多线程的,这会大大降低输出速度。