4

I am trying to create a print layout using PyQGIS. As part of that, I want to move my legend to the bottom right corner of the map, using the lower right corner of the legend as the reference point. Unfortunately I am struggling to get this to work. My code is shown below, including the printing I have been doing to troubleshoot.

layout = project.layoutManager().layoutByName(layout_name)
legend.setId('Legend')
layout.addLayoutItem(legend)
legend.setFrameEnabled(True)
legend.setFrameStrokeWidth(QgsLayoutMeasurement(0.3))
legend.setTitle('Legend')
fontsize = 16
legend.setStyleFont(QgsLegendStyle.Title, QFont("Arial", 24))
legend.setStyleFont(QgsLegendStyle.Group, QFont("Arial", fontsize))
legend.setStyleFont(QgsLegendStyle.Subgroup, QFont("Arial", fontsize))
legend.setStyleFont(QgsLegendStyle.Symbol, QFont("Arial", fontsize))
legend.setStyleFont(QgsLegendStyle.SymbolLabel, QFont("Arial", fontsize))
layout.addLayoutItem(legend)
legend.setResizeToContents(True)
legend.redraw()
legend.refreshItemSize()
print(legend.sizeWithUnits())
print(legend.referencePoint())
print(legend.pagePositionWithUnits())
legend.setReferencePoint(QgsLayoutItem.LowerRight)
legend.attemptMove(QgsLayoutPoint(409, 284, QgsUnitTypes.LayoutMillimeters), useReferencePoint=True)
print(legend.sizeWithUnits())
print(legend.referencePoint())
print(legend.pagePositionWithUnits())
legend.refreshItemPosition()

This gives the following readout:

<QgsLayoutSize: 0 x 0 mm >
0
<QgsLayoutPoint: 0, 0 mm >
<QgsLayoutSize: 0 x 0 mm >
8
<QgsLayoutPoint: 409, 284 mm >

From the printout, I can see that the reference point of the lower right corner has been correctly set, and that point has been moved correctly. The problem seems to be that for some reason my legend has no size! It renders correctly on the print layout (except for being in the wrong place).

Can anyone help me to understand why this might be the case, and what I could do to correct this?

Pictures below showing the result (top) and intended result (bottom)

actual result intended result

4
  • Can you add pictures with the result you get and the expected result ?
    – JULESG
    May 19, 2022 at 9:08
  • 2
    Added images as requested
    – GIS4good
    May 19, 2022 at 9:40
  • 1
    I don't have a solution, but basically the problem is, without size, your legend is placed at 409, 284mm from the top left corner of your page but it's just a pixel. No matter how you changed the reference point, it's a pixel so the reference is staying on that pixel because the size is 0,0mm. If you set the size with attemptResize(qgis.core.QgsLayoutSize(10, 10, qgis.core.QgsUnitTypes.LayoutMillimeters)) the reference point is going to change to 10mm from the Upper Left (default) to the Lower Left. It follows the size you specify, not the size you see on your screen.
    – JULESG
    May 19, 2022 at 10:31
  • Thank you for the answer. Yes this is also how I interpreted the print out from my posted code. The problem is that I want to automate creating maps with this code and so I don't know the size of the legend beforehand. I was hoping that the code snippets below would resize the legend to the contents and make this accessible in the code but this doesn't seem to be working and I don't understand why. legend.setResizeToContents(True) legend.redraw() legend.refreshItemSize()
    – GIS4good
    May 19, 2022 at 10:52

1 Answer 1

2

From your screenshot, it is clear that your legend is being added to your layout, "except for being in the wrong place". You haven't shown or explained how you calculated the layout point values for the attemptMove() parameter, so I would suspect this may be the problem. I would suggest that, instead of hardcoding the values, a better approach may be to retrieve the height and width values of either your layout page or map item and pass those subtracted by a small amount, to ensure that the legend is positioned correctly.

I tested using the code snippet below to position the lower right corner of the legend relative to the bottom right of a layout map item:

layout = QgsProject.instance().layoutManager().layoutByName('Test Layout')

# Retrieve width & height values of map item
map_item = [i for i in layout.items() if isinstance(i, QgsLayoutItemMap)][0]
map_width = map_item.sizeWithUnits().width()
map_height = map_item.sizeWithUnits().height()

legend = QgsLayoutItemLegend(layout)
layout.addLayoutItem(legend)
legend.setId('Legend')
layout.addLayoutItem(legend)
legend.setFrameEnabled(True)
legend.setFrameStrokeWidth(QgsLayoutMeasurement(0.3))
legend.setTitle('Legend')

legend.setReferencePoint(QgsLayoutItem.LowerRight)

# Pass map width & height values (subtract a small amount) for position of legend lower-right corner
legend.attemptMove(QgsLayoutPoint(map_width-1, map_height-1, QgsUnitTypes.LayoutMillimeters), useReferencePoint=True)

Result:

enter image description here

If you want to position the legend relative to the bottom right of the layout page, you can retrieve the page height and width with this snippet:

pages = layout.pageCollection()
page = pages.page(0)
page_width = page.pageSize().width()
page_height = page.pageSize().height()
2
  • Hi Ben, thank you for taking the time to answer. I have investigated further based on your answer and it has become clear that actually it is being generated in the right place when I check the created print layout via the GUI, but I am immediately exporting the layout to an image as part of the same piece of code and it seems that this prevents the legend location from updating correctly. I am experimenting with finding some way of refreshing everything before exporting but this doesn't seem to be working - it might be that I simply need to run two scripts, one to create and one to export.
    – GIS4good
    May 20, 2022 at 11:11
  • this is consistent with other indications that there is a refresh/timing issue which means that you can't get the layout item size in the same piece of code that creates it - you need to run code once to create it, and then again to access its size. see: gis.stackexchange.com/questions/371225/… , and gist.github.com/ThomasG77/…
    – GIS4good
    May 20, 2022 at 11:12

Your Answer

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

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