2

I'm adding functionality to a QGIS Plugin to create a print layout and add stuff to it like a map, legend, etc.

To test this code, I wrote a script. The script works and creates the print layout the way I want. Now I'm trying to add the code into the processAlgorithm() of my plugin's code. I'm getting different results with the same code from the script.

Here is what I have so far:

    def processAlgorithm(self, parameters, context, feedback):
        """This actually does the processing for creating the print layout and exporting as .pdf"""
        #needs all the arguments (self, parameters, context, feedback)

        log = feedback.setProgressText

        input_vector = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR, context)
        input_title = self.parameterAsString(parameters, self.INPUT_TITLE, context)

        log(f"Input vector name: {input_vector.name()}")            
        log(f"Title: {input_title}")                    


        """This creates a new print layout"""
        project = QgsProject.instance()             
        manager = project.layoutManager()           
        layout = QgsPrintLayout(project)            
        layoutName = "PrintLayout"

        layout = QgsPrintLayout(project)
        layout.initializeDefaults()                 
        layout.setName(layoutName)
        manager.addLayout(layout)



        """This adds a map item to the Print Layout"""
        map = QgsLayoutItemMap(layout)
        map.setRect(20, 20, 20, 20)  
        #Set Extent
        canvas = iface.mapCanvas()
        map.setExtent(canvas.extent())                  #sets map extent to current map canvas
        layout.addLayoutItem(map)
        #Move & Resize
        map.attemptMove(QgsLayoutPoint(5, 27, QgsUnitTypes.LayoutMillimeters))
        map.attemptResize(QgsLayoutSize(239, 178, QgsUnitTypes.LayoutMillimeters))

        results = {}
        return results

When I run this part of the code in the script, I get a nice map in my print layout. It looks like this: enter image description here

When I run my plugin with the code above, I get this: enter image description here This map is the correct size and in the correct position. But obviously there is no map and something is broken.

Here is the log after I run the plugin: enter image description here

Is the problem in the way I return the "results" from the processAlgorithm? In the QGIS documentation it says this: enter image description here

However, I don't want to return anything. I just want to create the print layout with items on it. There is no output from this part of the plugin.

I just create "results" variable as an empty dictionary and return it because I am supposed to return a dictionary. So obviously I am missing something.

8
  • Try moving the #Move & Resize section before the #Set Extent section.
    – Joseph
    Feb 19, 2019 at 15:17
  • @Joseph After doing what you suggest, this didn't work. I got a "rendering map" box and the box was the size of the extent of the map canvas, instead the size I defined using map.attemptResize Feb 19, 2019 at 15:51
  • Does your algorithm return the "no threading" flag? It's possible it's being run in a background thread, which will cause lots of issues, as print layouts are not thread safe and must all be modified from the main thread.
    – ndawson
    Feb 19, 2019 at 21:33
  • @ndawson no it doesn't. I have edited my question include the results log. Feb 20, 2019 at 0:10
  • Is there any particular reason why you are implementing this in a processing plugin/alorithm? Without knowing your precise use case, it strikes me that this would be better suited to a regular plugin, where you could modify the .ui file in Qt designer, for the user to select the input layer and layout title, then place the logic from your script in the run() method.
    – Ben W
    Feb 20, 2019 at 2:37

1 Answer 1

4

I think the reason is that your algorithm is being executed in a background thread, which is not allowed for print layouts. You'll need to adapt your algorithm and ensure it returns the FlagNoThreading flag:

     def flags(self):
        return super().flags() | QgsProcessingAlgorithm.FlagNoThreading
2
  • This works. Thank you Nyall! You touched on this in your comments, but could you say a little more about what this is doing? The documentation for QgsProcessingAlgorithm.FlagNoThreading doesn't say much. Feb 20, 2019 at 14:04
  • The c++ dox are a little more helpful here: "Algorithm is not thread safe and cannot be run in a background thread, e.g. for algorithms which manipulate the current project, layer selections, or with external dependencies which are not thread-safe."
    – ndawson
    Feb 21, 2019 at 0:32

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.