It looks like so hard without any script. So I tried a solution like below.
Sample data:

margin and interval

Script:
Copy/paste the script to QGIS Python Editor. Set layout_name
, layer_name
, path_field
, rc
, cc
and run.
import os
from qgis.PyQt.QtGui import QFont
##### MANUAL SETTINGS #####
layout_name = "LAYOUT_NAME"
layer_name = "Images"
path_field = "img_path"
rc, cc = 2, 2 # rc(row_count), cc(column_count)
interval = 10
margin = 20
###########################
project = QgsProject.instance()
# layout settings
layout_manager = project.layoutManager()
layouts = layout_manager.printLayouts()
# remove the same named layout
for layout in layouts:
if layout.name() == layout_name:
layout_manager.removeLayout(layout)
layout = QgsPrintLayout(project)
layout.setName(layout_name)
layout.initializeDefaults()
layout_manager.addLayout(layout)
iface.openLayoutDesigner(layout)
layer = project.mapLayersByName(layer_name)[0]
n = layer.featureCount()
### add pages
pcoll = layout.pageCollection()
pc = int(n /(cc*rc)) # required page count
for i in range(pc):
page = QgsLayoutItemPage(layout)
page.setPageSize('A4', QgsLayoutItemPage.Landscape)
pcoll.addPage(page)
###
pw = pcoll.pages()[0].pageSize().width() # page width
ph = pcoll.pages()[0].pageSize().height() # page height
w = (pw - (cc - 1) * interval - 2* margin) / cc # image width
h = (ph - (rc - 1) * interval - 2* margin) / rc # image height
print(w, h)
for i, f in enumerate(layer.getFeatures()):
r = (int(i / rc) % cc) # current row number, 0 indexed
c = i % cc # current column number, 0 indexed
pn = int(i / (cc * rc)) # current page number, 0 indexed
# calculate top-left coordinate for the image
x = margin + c * (interval + w)
y = margin + r * (interval + h)
# add image
img = QgsLayoutItemPicture(layout)
img.setPicturePath(f[path_field])
img.attemptMove(QgsLayoutPoint(x, y, 0), page=pn)
img.attemptResize(QgsLayoutSize(w, h, 0))
img.setFrameEnabled(True)
img.setPictureAnchor(QgsLayoutItem.Middle)
layout.addLayoutItem(img)
# calculate top-left coordinate for the label
x = margin + c * (interval + w)
y = margin + r * (interval + h) + h
# add label
label = QgsLayoutItemLabel(layout)
label.setText(os.path.basename(f[path_field]))
label.setFont(QFont('Arial', 14))
label.adjustSizeToText()
label.attemptMove(QgsLayoutPoint(x, y, 0), page=pn)
label.attemptResize(QgsLayoutSize(w, interval, 0))
label.setHAlign(Qt.AlignHCenter)
layout.addLayoutItem(label)
Demo:
