1

I'm trying to show a message bar in the main window, reporting on the result of a processing script. However, an empty message bar appears in the main window, and the text appears in a small little separate window, as below:

error screenshot

With a different message level (Warning) the blank message bar and the new window change their colour and icon accordingly:

enter image description here

But I prefer a normal message bar, all in one piece/place.

Anyone know what is missing here?

I am using iface.messageBar().pushMessage("Title","Message text") and have already imported:

from qgis.utils import iface

from qgis.gui import QgsMessageBar, QgisInterface

I have tried changing to QgisInterface.messageBar().pushMessage() with the same results, and have also tried importing some Qt modules just in case, but with no effect:

from qgis.PyQt.QtWidgets import (
    QSizePolicy,
    QPushButton,
    QDialog,
    QGridLayout,
    QDialogButtonBox)

The message bar works as expected when run from the Python console....

Here is a MWA (adapted from the normal processing script template):

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink)
from qgis import processing
from qgis.gui import QgsMessageBar, QgisInterface
from qgis.utils import iface

class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):

    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return ExampleProcessingAlgorithm()

    def name(self):
        return 'myscript'

    def displayName(self):
        return self.tr('My Script')

    def group(self):
        return self.tr('Example scripts')

    def groupId(self):
        return 'examplescripts'

    def initAlgorithm(self, config=None):

        # We add the input vector features source. It can have any kind of
        # geometry.
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Input layer'),
                [QgsProcessing.TypeVectorAnyGeometry]
        ))

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Output layer')
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(
            parameters,
            self.INPUT,
            context
        )

        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        (sink, dest_id) = self.parameterAsSink(
            parameters,
            self.OUTPUT,
            context,
            source.fields(),
            source.wkbType(),
            source.sourceCrs()
        )

        # Send some information to the user
        feedback.pushInfo(f'CRS is {source.sourceCrs().authid()}')

        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

        for current, feature in enumerate(features):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            # Add a feature in the sink
            sink.addFeature(feature, QgsFeatureSink.FastInsert)

            # Update the progress bar
            feedback.setProgress(int(current * total))

        if False:
            buffered_layer = processing.run("native:buffer", {
                'INPUT': dest_id,
                'DISTANCE': 1.5,
                'SEGMENTS': 5,
                'END_CAP_STYLE': 0,
                'JOIN_STYLE': 0,
                'MITER_LIMIT': 2,
                'DISSOLVE': False,
                'OUTPUT': 'memory:'
            }, context=context, feedback=feedback)['OUTPUT']

        iface.messageBar().pushMessage("Title","Message text")
        return {self.OUTPUT: dest_id}

  • The little window closes if you close the message bar, and vice versa.
  • It happens when run from a saved script in the processing toolbox, and when running from the script editor.
  • It still happens with only QGIS window open.
5
  • 3
    It's probably a wonder that your QGIS is not crashing. Your problem is explained here, because iface.messageBar().pushMessage() is a non thread safe api call. I suggest that you use the QgsProcessingFeedback class as described here or implement the flags() method and return QgsProcessingAlgorithm.FlagNoThreading.
    – Ben W
    Commented Nov 7, 2023 at 11:12
  • QgsMessageBar
    – Andre Geo
    Commented Nov 7, 2023 at 11:13
  • @Taras QgsMessageBar @BenW thanks. The problem is completely solved by not threading the algorithm. Only drawback is that the main window is frozen while the algorithm runs.. Is there a way to show the messageBar in the algorithm's Parameter/Log window? The reason I prefer messageBar instead of processing feedback, is that the messageBar is more "user-friendly" than the logs.
    – Andre Geo
    Commented Nov 7, 2023 at 11:45
  • Or is it possible to trigger iface.messageBar().pushMessage() in the main thread or in the console, after the algorithm thread has finished?
    – Andre Geo
    Commented Nov 7, 2023 at 11:56
  • 1
    After the algorithm has finished you can use postProcessAlgorithm() method, which is called from the main thread.
    – Ben W
    Commented Nov 7, 2023 at 12:10

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.