9

I'm trying to create, reposition and resize an HTML frame in a QGIS 3.2.0 Print Composer layout using PyQGIS:

proj = QgsProject.instance()
comp = QgsPrintLayout(proj)
blockStats = QgsLayoutItemHtml.create(comp)
blockStats.setFixedSize(QgsLayoutSize(34.3,25, QgsUnitTypes.LayoutMillimeters))
blockStats.attemptMove(QgsLayoutPoint(200,89, QgsUnitTypes.LayoutMillimeters))
blockStats.setFrameEnabled(True)
blockStats.setFrameStrokeWidth(QgsLayoutMeasurement(0.5,QgsUnitTypes.LayoutMillimeters))
comp.addLayoutItem(blockStats)

Lines 4 and 5 above give me "object has no attribute" errors for 'setFixedSize' and 'attemptMove' - how can I reposition and resize this object?


Using the answer by @ndawson I've revised my code as follows:

proj = QgsProject.instance()
comp = QgsPrintLayout(proj)
blockStatsHTML = QgsLayoutItemHtml.create(comp)
blockStatsHTML.setHtml("test<br><b>test</b>")
blockStatsHTML.loadHtml()
blockStatsFrame = QgsLayoutFrame(comp, blockStatsHTML)
blockStatsHTML.addFrame(blockStatsFrame)
blockStatsFrame.setFixedSize(QgsLayoutSize(34.3,25, QgsUnitTypes.LayoutMillimeters))
blockStatsFrame.attemptMove(QgsLayoutPoint(100,100, QgsUnitTypes.LayoutMillimeters))
blockStatsFrame.setFrameEnabled(True)
blockStatsFrame.setFrameStrokeWidth(QgsLayoutMeasurement(0.5, QgsUnitTypes.LayoutMillimeters))
comp.addLayoutItem(blockStatsFrame)
comp.moveItemToTop(blockStatsFrame)

I'm no longer getting Python errors, however the frame still isn't appearing in the print layout. I've tried changing the position, using positive and negative coordinates, but I can't make it show up.

Is there another step required to make it visible?


Just an update and clarification - I've added the following line at the end of the code:

lmgr.addLayout(comp)

When I open the layout in the print composer, the Items Panel contains only "Map 1" - there is no listing for the blockStatsFrame or blockStatsHTML item. So this tells me any other attempts at removing or resizing the item would be futile, since it doesn't exist in the layout as far as the print composer is concerned.

Apart from comp.addLayoutItem(blockStatsFrame), should anything else be necessary in order to add the item to the layout?


In case anyone is facing the same issue, the solution for me was in using attemptSetSceneRect to set the dimensions of the frame. Without this, the frame didn't appear in the layout at all. Note however that after using this method, setFixedSize doesn't appear to work. Relevant parts of my working code are below:

proj = QgsProject.instance()
lmgr = proj.layoutManager()
comp = QgsPrintLayout(proj)

# create block stats HTML multiframe
blockStatsHTML = QgsLayoutItemHtml.create(comp)

# create frame to show content from blockStatsHTML
blockStatsFrame = QgsLayoutFrame(comp, blockStatsHTML)
blockStatsFrame.attemptSetSceneRect(QRectF(10, 10, 30, 20))
blockStatsFrame.setFrameEnabled(True)
blockStatsHTML.addFrame(blockStatsFrame)

# set HTML contents
blockStatsHTML.setContentMode(QgsLayoutItemHtml.ManualHtml)
blockStatsHTML.setHtml("test<br><b>test</b>")
blockStatsHTML.loadHtml()

# reposition blockStatsFrame
blockStatsFrame.attemptMove(QgsLayoutPoint(10,10, QgsUnitTypes.LayoutMillimeters))

# set frame outline
blockStatsFrame.setFrameStrokeColor(QColor(255, 0, 255))
blockStatsFrame.setFrameStrokeWidth(QgsLayoutMeasurement(1, QgsUnitTypes.LayoutMillimeters))

# set frame background                        
blockStatsFrame.setBackgroundEnabled(True)
blockStatsFrame.setBackgroundColor(QColor(255, 255, 255))

# add frame to layout
comp.addLayoutItem(blockStatsFrame)

# add layout to layout manager
lmgr.addLayout(comp)

3 Answers 3

6

HTML layout items are "multi frame" items, which means that their content is split over multiple QgsLayoutFrame items.

After creating your QgsLayoutItemHtml, you need to then create frames to display the content. E.g.:

blockStats = QgsLayoutItemHtml.create(comp)

# create a frame for showing content from blockStats
frame1 = QgsLayoutFrame(comp, blockStats)
blockStats.addFrame(frame1)

# and resize/move the frame
frame1.setFixedSize(QgsLayoutSize(34.3,25, QgsUnitTypes.LayoutMillimeters))
frame1.attemptMove(QgsLayoutPoint(200,89, QgsUnitTypes.LayoutMillimeters))
1
  • 1
    Brilliant - thanks Nyall, this has been really helpful. I'm not getting any errors now, but the frame isn't showing up on the print layout - maybe there's another step I'm missing. I'll edit the question to show my updated code. Commented Aug 17, 2018 at 1:57
5
+50

Gareth, I gleaned a lot from Nyall's most-excellent test code over here Try something like this.

from PyQt5.QtCore import QRectF
... 
layout = lmgr.layoutByName("your_layout_name")
...
layout_html = QgsLayoutItemHtml(layout)
html_frame = QgsLayoutFrame(layout, layout_html)
html_frame.attemptSetSceneRect(QRectF(10, 10, 30, 20))
html_frame.setFrameEnabled(True)
layout_html.addFrame(html_frame)
layout_html.setContentMode(QgsLayoutItemHtml.ManualHtml)
layout_html.setHtml('test<br><b>test</b>')
layout_html.loadHtml()
html_frame.setFrameStrokeWidth(QgsLayoutMeasurement(0.5, QgsUnitTypes.LayoutMillimeters))
2
  • Fantastic. It works! The key appears to have been in the html_frame.attemptSetSceneRect(QRectF(10, 10, 30, 20)) line - without this, the frame doesn't show up at all. This has solved a huge problem for me - thanks a lot. Will update the code in my question to show the final solution. Commented Sep 2, 2018 at 22:03
  • not quite working in 3.16.14. here's a version that works for me:
    – cefect
    Commented Feb 16, 2022 at 13:48
2

@cm1's solution is not quite working in 3.16.14 for me. here's a version that works for me:

#get references
proj = QgsProject.instance()
lmgr = proj.layoutManager()
from PyQt5.QtCore import QRectF

#build the layouts 
layout = QgsPrintLayout(proj)
layout.setName("your_layout_name")
layout_html = QgsLayoutItemHtml(layout)

#add the frame
html_frame = QgsLayoutFrame(layout, layout_html)
html_frame.attemptSetSceneRect(QRectF(10, 10, 30, 20))
html_frame.setFrameEnabled(True)
layout_html.addFrame(html_frame)

#populate layout
layout_html.setContentMode(QgsLayoutItemHtml.ManualHtml)
layout_html.setHtml('test<br><b>test</b>')
layout_html.loadHtml()
html_frame.setFrameStrokeWidth(QgsLayoutMeasurement(0.5, QgsUnitTypes.LayoutMillimeters))

#add layout to project
lmgr.addLayout(layout)

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