194 lines
7.8 KiB
Python
Executable File
194 lines
7.8 KiB
Python
Executable File
#! python3 # noqa: E265
|
|
|
|
# standard library
|
|
import logging
|
|
from functools import partial
|
|
from typing import Callable, Literal, Optional, Union
|
|
|
|
# PyQGIS
|
|
from qgis.core import Qgis, QgsMessageLog, QgsMessageOutput
|
|
from qgis.gui import QgsMessageBar
|
|
from qgis.PyQt.QtWidgets import QPushButton, QWidget
|
|
from qgis.utils import iface
|
|
|
|
# project package
|
|
import gn_tools.toolbelt.preferences as plg_prefs_hdlr
|
|
from gn_tools.__about__ import __title__
|
|
|
|
# ############################################################################
|
|
# ########## Classes ###############
|
|
# ##################################
|
|
|
|
|
|
class PlgLogger(logging.Handler):
|
|
"""Python logging handler supercharged with QGIS useful methods."""
|
|
|
|
@staticmethod
|
|
def log(
|
|
message: str,
|
|
application: str = __title__,
|
|
log_level: Union[
|
|
Qgis.MessageLevel, Literal[0, 1, 2, 3, 4]
|
|
] = Qgis.MessageLevel.Info,
|
|
push: bool = False,
|
|
duration: Optional[int] = None,
|
|
# widget
|
|
button: bool = False,
|
|
button_text: Optional[str] = None,
|
|
button_more_text: Optional[str] = None,
|
|
button_connect: Optional[Callable] = None,
|
|
# parent
|
|
parent_location: Optional[QWidget] = None,
|
|
):
|
|
"""Send messages to QGIS messages windows and to the user as a message bar. \
|
|
Plugin name is used as title. If debug mode is disabled, only warnings (1) and \
|
|
errors (2) or with push are sent.
|
|
|
|
:param message: message to display
|
|
:type message: str
|
|
:param application: name of the application sending the message. \
|
|
Defaults to __about__.__title__
|
|
:type application: str, optional
|
|
:param log_level: message level. Possible values: any values of enum \
|
|
`Qgis.MessageLevel`. For legacy purposes, it's also possible to pass \
|
|
corresponding integers but it's not recommended anymore. Legacy values: \
|
|
0 (info), 1 (warning), 2 (critical), 3 (success), 4 (none - grey). Defaults \
|
|
to Qgis.MessageLevel(0) (info)
|
|
:type log_level: Union[Qgis.MessageLevel, Literal[0, 1, 2, 3, 4]], optional
|
|
:param push: also display the message in the QGIS message bar in addition to \
|
|
the log, defaults to False
|
|
:type push: bool, optional
|
|
:param duration: duration of the message in seconds. If not set, the \
|
|
duration is calculated from the log level: `(log_level + 1) * 3`. seconds. \
|
|
If set to 0, then the message must be manually dismissed by the user. \
|
|
Defaults to None.
|
|
:type duration: int, optional
|
|
:param button: display a button in the message bar. Defaults to False.
|
|
:type button: bool, optional
|
|
:param button_text: text label of the button. Defaults to None.
|
|
:type button_text: str, optional
|
|
:param button_more_text: text to display within the QgsMessageOutput
|
|
:type button_more_text: str, optional
|
|
:param button_connect: function to be called when the button is pressed. \
|
|
If not set, a simple dialog (QgsMessageOutput) is used to dislay the message. \
|
|
Defaults to None.
|
|
:type button_connect: Callable, optional
|
|
:param parent_location: parent location widget. \
|
|
If not set, QGIS canvas message bar is used to push message, \
|
|
otherwise if a QgsMessageBar is available in parent_location it is used instead. \
|
|
Defaults to None.
|
|
:type parent_location: Widget, optional
|
|
|
|
:Example:
|
|
|
|
.. code-block:: python
|
|
|
|
# using enums from Qgis:
|
|
# Qgis.Info, Qgis.MessageLevel.Warning, Qgis.MessageLevel.Critical, Qgis.MessageLevel.Success, Qgis.MessageLevel.NoLevel
|
|
from qgis.core import Qgis
|
|
|
|
log(message="Plugin loaded - INFO", log_level=Qgis.MessageLevel.Info, push=False)
|
|
log(
|
|
message="Something went wrong but it's not blocking",
|
|
log_level=Qgis.MessageLevel.Warning
|
|
)
|
|
log(
|
|
message="Plugin failed to load - CRITICAL",
|
|
log_level=Qgis.MessageLevel(2),
|
|
push=True
|
|
)
|
|
|
|
# LEGACY - using integers:
|
|
log(message="Plugin loaded - INFO", log_level=Qgis.MessageLevel.Info, push=False)
|
|
log(message="Plugin loaded - WARNING", log_level=Qgis.MessageLevel.Warning, push=1, duration=5)
|
|
log(message="Plugin loaded - ERROR", log_level=Qgis.MessageLevel.Critical, push=1, duration=0)
|
|
log(
|
|
message="Plugin loaded - SUCCESS",
|
|
log_level=Qgis.MessageLevel.Success,
|
|
push=1,
|
|
duration=10,
|
|
button=True
|
|
)
|
|
log(
|
|
message="Plugin loaded",
|
|
log_level=Qgis.MessageLevel.Critical,
|
|
push=1,
|
|
duration=0
|
|
button=True,
|
|
button_label=self.tr("See details"),
|
|
button_more_text=detailed_error_message
|
|
)
|
|
log(message="Plugin loaded - TEST", log_level=Qgis.MessageLevel.NoLevel, push=0)
|
|
"""
|
|
# if not debug mode and not push, let's ignore INFO, SUCCESS and TEST
|
|
debug_mode = plg_prefs_hdlr.PlgOptionsManager.get_plg_settings().debug_mode
|
|
if not debug_mode and not push and (log_level < 1 or log_level > 2):
|
|
return
|
|
|
|
# if log_level is an int, convert it to Qgis.MessageLevel
|
|
if isinstance(log_level, int):
|
|
log_level = Qgis.MessageLevel(log_level)
|
|
|
|
# ensure message is a string
|
|
if not isinstance(message, str):
|
|
try:
|
|
message = str(message)
|
|
except Exception as err:
|
|
err_msg = "Log message must be a string, not: {}. Trace: {}".format(
|
|
type(message), err
|
|
)
|
|
logging.error(err_msg)
|
|
message = err_msg
|
|
|
|
# send it to QGIS messages panel
|
|
QgsMessageLog.logMessage(
|
|
message=message, tag=application, notifyUser=push, level=log_level
|
|
)
|
|
|
|
# optionally, display message on QGIS Message bar (above the map canvas)
|
|
if push and iface is not None:
|
|
msg_bar = None
|
|
|
|
# QGIS or custom dialog
|
|
if parent_location and isinstance(parent_location, QWidget):
|
|
msg_bar = parent_location.findChild(QgsMessageBar)
|
|
|
|
if not msg_bar:
|
|
msg_bar = iface.messageBar()
|
|
|
|
# calc duration
|
|
if duration is None:
|
|
duration = (log_level + 1) * 3
|
|
|
|
# create message with/out a widget
|
|
if button:
|
|
# create output message
|
|
notification = iface.messageBar().createMessage(
|
|
title=application, text=message
|
|
)
|
|
widget_button = QPushButton(button_text or "More...")
|
|
if button_connect:
|
|
widget_button.clicked.connect(button_connect)
|
|
else:
|
|
mini_dlg: QgsMessageOutput = QgsMessageOutput.createMessageOutput()
|
|
mini_dlg.setTitle(application)
|
|
mini_dlg.setMessage(
|
|
f"{message}\n{button_more_text}",
|
|
QgsMessageOutput.MessageType.MessageText,
|
|
)
|
|
widget_button.clicked.connect(partial(mini_dlg.showMessage, False))
|
|
|
|
notification.layout().addWidget(widget_button)
|
|
msg_bar.pushWidget(
|
|
widget=notification, level=log_level, duration=duration
|
|
)
|
|
|
|
else:
|
|
# send simple message
|
|
msg_bar.pushMessage(
|
|
title=application,
|
|
text=message,
|
|
level=log_level,
|
|
duration=duration,
|
|
)
|