PyQGIS – Creating and Manipulating HTML Frame in QGIS 3.2.0 Print Composer

print-composerpyqgispyqgis-3qgis-3

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)

Best Answer

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))
Related Question