[QGIS-Developer] QGIS composer print layout

Giulio Fattori giulio.fattori at tin.it
Sun Jan 24 04:17:22 PST 2021


Il 23/01/2021 15:35, JD L ha scritto:
> Hi Giulio,
>
> add the flags methods to tell your algorithm is not thread-safe. From 
> the documentation :
> https://docs.qgis.org/3.16/en/docs/user_manual/processing/scripts.html#flags 
> <https://docs.qgis.org/3.16/en/docs/user_manual/processing/scripts.html#flags>
>
> If your algorithm is regularly crashing, you are probably using API 
> calls which are not safe to do in a background thread. Try returning 
> the *QgsProcessingAlgorithm.FlagNoThreading flag from your algorithm’s 
> flags() method* to force Processing to run your algorithm in the main 
> thread instead.
>
> I just tested and it works.
>
> Regards,
>
> Le ven. 22 janv. 2021 à 14:56, Giulio Fattori <giulio.fattori at tin.it 
> <mailto:giulio.fattori at tin.it>> a écrit :
>
>     Hello,
>     i'm developing a QGIS plugin algorithm that composes a print layout.
>     The attached code works but the map window appears in the composer
>     with the word "Rendering map".
>     and I get these messages:
>
>     WARNING    QObject::connect: Cannot queue arguments of type
>     'QItemSelection'
>                  (Make sure 'QItemSelection' is registered using
>     qRegisterMetaType().)
>     WARNING    QObject::connect: Cannot queue arguments of type
>     'QItemSelection'
>                  (Make sure 'QItemSelection' is registered using
>     qRegisterMetaType().)
>     WARNING    QObject::connect: Cannot queue arguments of type
>     'QItemSelection'
>                  (Make sure 'QItemSelection' is registered using
>     qRegisterMetaType().)
>     WARNING    QObject::startTimer: Timers cannot be started from
>     another thread
>
>     CRITICAL    Qt : QObject::killTimer: Timers cannot be stopped from
>     another thread
>     CRITICAL    Qt : QObject::startTimer: Timers cannot be started
>     from another thread
>     CRITICAL    Qt : QObject::killTimer: Timers cannot be stopped from
>     another thread
>     CRITICAL    Qt : QObject::startTimer: Timers can only be used with
>     threads started with QThread
>
>     Only after saving and reopening QGIS do I get the correct result
>     in the composer.
>
>     What is missing to make it work properly?
>
>     Can someone help me?
>
>     _______________________________________________
>     QGIS-Developer mailing list
>     QGIS-Developer at lists.osgeo.org <mailto:QGIS-Developer at lists.osgeo.org>
>     List info: https://lists.osgeo.org/mailman/listinfo/qgis-developer
>     <https://lists.osgeo.org/mailman/listinfo/qgis-developer>
>     Unsubscribe:
>     https://lists.osgeo.org/mailman/listinfo/qgis-developer
>     <https://lists.osgeo.org/mailman/listinfo/qgis-developer>
>

Hi Daniel,

thanks for your kind response, could you send me the code correct as you 
say?

Regards

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/qgis-developer/attachments/20210124/7571e529/attachment-0001.html>
-------------- next part --------------
# -*- coding: utf-8 -*-

"""
***************************************************************************
*   Korto19 - last modified 17.01.2020/2021 19.01      
*   Put a map to composer
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""
from PyQt5.QtGui import QFont
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProject,
                       QgsPrintLayout,
                       QgsLayoutItemPage,
                       QgsLayoutItemMap,
                       QgsLayoutPoint,
                       QgsUnitTypes,
                       QgsLayoutSize,
                       QgsRectangle,
                       QgsLayoutItemLabel,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink)
                       
from qgis import processing
from qgis.utils import iface

import math

class TestMapProcessingAlgorithm(QgsProcessingAlgorithm):
    """
    """

    # Constants used to refer to parameters and outputs. They will be
    # used when calling the algorithm from another algorithm, or when
    # calling from the QGIS console.

    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'

    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return TestMapProcessingAlgorithm()

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'Test Map'

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return self.tr('Test Map')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr('Test Map')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'Test Map'

    def shortHelpString(self):
        """
        Returns a localised short helper string for the algorithm. This string
        should provide a basic description about what the algorithm does and the
        parameters and outputs associated with it..
        """
        return self.tr("Test map composition")

    def initAlgorithm(self, config=None):
        """
        Here we define the inputs and output of the algorithm, along
        with some other properties.
        """

        # 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]
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        source = self.parameterAsLayer(
            parameters,
            self.INPUT,
            context
        )

        #Inizialize references
        project = QgsProject.instance()             #gets a reference to the project instance
        manager = project.layoutManager()           #gets a reference to the layout manager
        root = project.layerTreeRoot()              #get reference to tree root

        #set pagesize, layout name and scale (fit or numeric)
        pagesize = 'A3'
        layoutName = "CompoMap_" + pagesize + ' ' + source.name()
        map_scala = 'fit'

        #If exist remove layout
        layouts_list = manager.printLayouts()
        for layout in layouts_list:
            if layout.name() == layoutName:
                manager.removeLayout(layout)

        #create layout
        layout = QgsPrintLayout(project)
        layout.initializeDefaults()
        layout.setName(layoutName)
        layout.pageCollection().page(0).setPageSize(pagesize, QgsLayoutItemPage.Orientation.Portrait)
        manager.addLayout(layout)

        #calculates the size of the map minus the edges
        xm = (layout.pageCollection().page(0).pageSize().width()-10)
        ym = (layout.pageCollection().page(0).pageSize().height()-10)
        
        #set layer active and fit map
        iface.setActiveLayer(source)
        root.findLayer(source.id()).setItemVisibilityChecked(True)
        canvas = iface.mapCanvas()
        canvas.setExtent(source.extent())
        canvas.refresh()

        #add map to layout and set position
        map = QgsLayoutItemMap(layout)
        map.setRect(20, 20, 20, 20)
        map.setExtent(canvas.extent())
        map.setId(source.name())
        map.attemptMove(QgsLayoutPoint(5, 5, QgsUnitTypes.LayoutMillimeters))
        map.attemptResize(QgsLayoutSize(xm, ym, QgsUnitTypes.LayoutMillimeters))
                
        #override scale Test_map
        if map_scala == 'fit':
            h_map_scala = math.ceil(source.extent().height()/map.rect().height())*1000
            w_map_scala = math.ceil(source.extent().width() /map.rect().width())*1000
            map_scala = max(h_map_scala,w_map_scala)

        extent = map.extent()
        center = extent.center()
        newwidth  = extent.width()  / map.scale() * map_scala
        newheight = extent.height() / map.scale() * map_scala
        x1 = center.x() - 0.5 * newwidth
        y1 = center.y() - 0.5 * newheight
        x2 = center.x() + 0.5 * newwidth
        y2 = center.y() + 0.5 * newheight
        map.setExtent(QgsRectangle(x1, y1, x2, y2))

        #add map to layout
        layout.addLayoutItem(map)

        #select and lock map set
        map.setLayers([source])
        map.setKeepLayerSet(True)
        map.setKeepLayerStyles(True)
        map.setBackgroundEnabled(False)
            
        #Add title
        title = QgsLayoutItemLabel(layout)
        title.setText(source.name())
        title.setFont(QFont("Arial Black", 12))
        title.adjustSizeToText()
        title.attemptMove(QgsLayoutPoint(5, 5, QgsUnitTypes.LayoutMillimeters))
        layout.addLayoutItem(title)
        
        feedback.pushInfo("WOW The plugin is working as it should, created " + layoutName)
        
        return {self.OUTPUT: None}


More information about the QGIS-Developer mailing list