Example #1
0
    def testRemoveLayout(self):
        project = QgsProject()
        layout = QgsPrintLayout(project)
        layout.setName('test layout')

        self.manager = QgsLayoutManager(project)
        layout_removed_spy = QSignalSpy(self.manager.layoutRemoved)
        layout_about_to_be_removed_spy = QSignalSpy(self.manager.layoutAboutToBeRemoved)
        # tests that layout still exists when layoutAboutToBeRemoved is fired
        self.manager.layoutAboutToBeRemoved.connect(self.layoutAboutToBeRemoved)

        # not added, should fail
        self.assertFalse(self.manager.removeLayout(layout))
        self.assertEqual(len(layout_removed_spy), 0)
        self.assertEqual(len(layout_about_to_be_removed_spy), 0)

        self.assertTrue(self.manager.addLayout(layout))
        self.assertEqual(self.manager.layouts(), [layout])
        self.assertTrue(self.manager.removeLayout(layout))
        self.assertEqual(len(self.manager.layouts()), 0)
        self.assertEqual(len(layout_removed_spy), 1)
        self.assertEqual(layout_removed_spy[0][0], 'test layout')
        self.assertEqual(len(layout_about_to_be_removed_spy), 1)
        self.assertEqual(layout_about_to_be_removed_spy[0][0], 'test layout')
        self.assertTrue(self.aboutFired)
        self.manager = None
Example #2
0
    def testAddLayout(self):
        project = QgsProject()
        layout = QgsPrintLayout(project)
        layout.setName('test layout')

        manager = QgsLayoutManager(project)

        layout_about_to_be_added_spy = QSignalSpy(manager.layoutAboutToBeAdded)
        layout_added_spy = QSignalSpy(manager.layoutAdded)
        self.assertTrue(manager.addLayout(layout))
        self.assertEqual(len(layout_about_to_be_added_spy), 1)
        self.assertEqual(layout_about_to_be_added_spy[0][0], 'test layout')
        self.assertEqual(len(layout_added_spy), 1)
        self.assertEqual(layout_added_spy[0][0], 'test layout')

        # adding it again should fail
        self.assertFalse(manager.addLayout(layout))

        # try adding a second layout
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        self.assertTrue(manager.addLayout(layout2))
        self.assertEqual(len(layout_added_spy), 2)
        self.assertEqual(layout_about_to_be_added_spy[1][0], 'test layout2')
        self.assertEqual(len(layout_about_to_be_added_spy), 2)
        self.assertEqual(layout_added_spy[1][0], 'test layout2')

        # adding a layout with duplicate name should fail
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout2')
        self.assertFalse(manager.addLayout(layout3))
Example #3
0
    def testReadWriteXml(self):
        """
        Test reading and writing layout manager state to XML
        """
        project = QgsProject()
        manager = QgsLayoutManager(project)

        # add a bunch of layouts
        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout3')

        manager.addLayout(layout)
        manager.addLayout(layout2)
        manager.addLayout(layout3)

        # save to xml
        doc = QDomDocument("testdoc")
        elem = manager.writeXml(doc)
        doc.appendChild(elem)

        # restore from xml
        project2 = QgsProject()
        manager2 = QgsLayoutManager(project2)
        self.assertTrue(manager2.readXml(elem, doc))

        self.assertEqual(len(manager2.layouts()), 3)
        names = [c.name() for c in manager2.layouts()]
        self.assertCountEqual(names, ['test layout', 'test layout2', 'test layout3'])
Example #4
0
    def testExpressionInText(self):
        """Test expressions embedded in legend node text"""

        point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
        point_layer = QgsVectorLayer(point_path, 'points', 'ogr')

        layout = QgsPrintLayout(QgsProject.instance())
        layout.setName('LAYOUT')
        layout.initializeDefaults()

        map = QgsLayoutItemMap(layout)
        map.attemptSetSceneRect(QRectF(20, 20, 80, 80))
        map.setFrameEnabled(True)
        map.setLayers([point_layer])
        layout.addLayoutItem(map)
        map.setExtent(point_layer.extent())

        legend = QgsLayoutItemLegend(layout)
        legend.setTitle("Legend")
        legend.attemptSetSceneRect(QRectF(120, 20, 100, 100))
        legend.setFrameEnabled(True)
        legend.setFrameStrokeWidth(QgsLayoutMeasurement(2))
        legend.setBackgroundColor(QColor(200, 200, 200))
        legend.setTitle('')
        legend.setLegendFilterByMapEnabled(False)
        legend.setStyleFont(QgsLegendStyle.Title, QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Group, QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Subgroup, QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Symbol, QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.SymbolLabel, QgsFontUtils.getStandardTestFont('Bold', 16))

        legend.setAutoUpdateModel(False)

        QgsProject.instance().addMapLayers([point_layer])
        s = QgsMapSettings()
        s.setLayers([point_layer])

        group = legend.model().rootGroup().addGroup("Group [% 1 + 5 %] [% @layout_name %]")
        layer_tree_layer = group.addLayer(point_layer)
        layer_tree_layer.setCustomProperty("legend/title-label", 'bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]')
        QgsMapLayerLegendUtils.setLegendNodeUserLabel(layer_tree_layer, 0, 'xxxx')
        legend.model().refreshLayerLegend(layer_tree_layer)
        legend.model().layerLegendNodes(layer_tree_layer)[0].setUserLabel('bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]')

        layout.addLayoutItem(legend)
        legend.setLinkedMap(map)

        map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))

        checker = QgsLayoutChecker(
            'composer_legend_expressions', layout)
        checker.setControlPathPrefix("composer_legend")
        result, message = checker.testLayout()
        self.assertTrue(result, message)

        QgsProject.instance().removeMapLayers([point_layer.id()])
Example #5
0
    def testClear(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)

        # add a bunch of layouts
        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout3')

        manager.addLayout(layout)
        manager.addLayout(layout2)
        manager.addLayout(layout3)

        layout_removed_spy = QSignalSpy(manager.layoutRemoved)
        layout_about_to_be_removed_spy = QSignalSpy(manager.layoutAboutToBeRemoved)
        manager.clear()
        self.assertEqual(len(manager.layouts()), 0)
        self.assertEqual(len(layout_removed_spy), 3)
        self.assertEqual(len(layout_about_to_be_removed_spy), 3)
Example #6
0
    def testExpressionInText(self):
        """Test expressions embedded in legend node text"""

        point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
        point_layer = QgsVectorLayer(point_path, 'points', 'ogr')

        layout = QgsPrintLayout(QgsProject.instance())
        layout.setName('LAYOUT')
        layout.initializeDefaults()

        map = QgsLayoutItemMap(layout)
        map.attemptSetSceneRect(QRectF(20, 20, 80, 80))
        map.setFrameEnabled(True)
        map.setLayers([point_layer])
        layout.addLayoutItem(map)
        map.setExtent(point_layer.extent())

        legend = QgsLayoutItemLegend(layout)
        legend.setTitle("Legend")
        legend.attemptSetSceneRect(QRectF(120, 20, 100, 100))
        legend.setFrameEnabled(True)
        legend.setFrameStrokeWidth(QgsLayoutMeasurement(2))
        legend.setBackgroundColor(QColor(200, 200, 200))
        legend.setTitle('')
        legend.setLegendFilterByMapEnabled(False)
        legend.setStyleFont(QgsLegendStyle.Title,
                            QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Group,
                            QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Subgroup,
                            QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.Symbol,
                            QgsFontUtils.getStandardTestFont('Bold', 16))
        legend.setStyleFont(QgsLegendStyle.SymbolLabel,
                            QgsFontUtils.getStandardTestFont('Bold', 16))

        legend.setAutoUpdateModel(False)

        QgsProject.instance().addMapLayers([point_layer])
        s = QgsMapSettings()
        s.setLayers([point_layer])

        group = legend.model().rootGroup().addGroup(
            "Group [% 1 + 5 %] [% @layout_name %]")
        layer_tree_layer = group.addLayer(point_layer)
        layer_tree_layer.setCustomProperty(
            "legend/title-label",
            'bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]')
        QgsMapLayerLegendUtils.setLegendNodeUserLabel(layer_tree_layer, 0,
                                                      'xxxx')
        legend.model().refreshLayerLegend(layer_tree_layer)
        legend.model().layerLegendNodes(layer_tree_layer)[0].setUserLabel(
            'bbbb [% 1+2 %] xx [% @layout_name %] [% @layer_name %]')

        layout.addLayoutItem(legend)
        legend.setLinkedMap(map)

        map.setExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))

        checker = QgsLayoutChecker('composer_legend_expressions', layout)
        checker.setControlPathPrefix("composer_legend")
        result, message = checker.testLayout()
        self.assertTrue(result, message)

        QgsProject.instance().removeMapLayers([point_layer.id()])
Example #7
0
    def testReadWriteXml(self):
        p = QgsProject()
        l = QgsPrintLayout(p)
        l.setName('my layout')
        l.setUnits(QgsUnitTypes.LayoutInches)
        collection = l.pageCollection()

        # add a page
        page = QgsLayoutItemPage(l)
        page.setPageSize('A6')
        collection.addPage(page)

        grid = l.gridSettings()
        grid.setResolution(QgsLayoutMeasurement(5, QgsUnitTypes.LayoutPoints))

        g1 = QgsLayoutGuide(
            Qt.Horizontal,
            QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters),
            l.pageCollection().page(0))
        l.guides().addGuide(g1)

        snapper = l.snapper()
        snapper.setSnapTolerance(7)

        # add some items
        item1 = QgsLayoutItemMap(l)
        item1.setId('xxyyxx')
        l.addItem(item1)
        item2 = QgsLayoutItemMap(l)
        item2.setId('zzyyzz')
        l.addItem(item2)

        l.setReferenceMap(item2)

        doc = QDomDocument("testdoc")
        elem = l.writeXml(doc, QgsReadWriteContext())

        l2 = QgsPrintLayout(p)
        self.assertTrue(l2.readXml(elem, doc, QgsReadWriteContext()))
        self.assertEqual(l2.name(), 'my layout')
        self.assertEqual(l2.units(), QgsUnitTypes.LayoutInches)

        collection2 = l2.pageCollection()
        self.assertEqual(collection2.pageCount(), 1)
        self.assertAlmostEqual(collection2.page(0).pageSize().width(), 105, 4)
        self.assertEqual(collection2.page(0).pageSize().height(), 148)
        self.assertEqual(l2.gridSettings().resolution().length(), 5.0)
        self.assertEqual(l2.gridSettings().resolution().units(),
                         QgsUnitTypes.LayoutPoints)
        self.assertEqual(l2.guides().guidesOnPage(0)[0].orientation(),
                         Qt.Horizontal)
        self.assertEqual(l2.guides().guidesOnPage(0)[0].position().length(),
                         5.0)
        self.assertEqual(l2.guides().guidesOnPage(0)[0].position().units(),
                         QgsUnitTypes.LayoutCentimeters)
        self.assertEqual(l2.snapper().snapTolerance(), 7)

        # check restored items
        new_item1 = l2.itemByUuid(item1.uuid())
        self.assertTrue(new_item1)
        self.assertEqual(new_item1.id(), 'xxyyxx')
        new_item2 = l2.itemByUuid(item2.uuid())
        self.assertTrue(new_item2)
        self.assertEqual(new_item2.id(), 'zzyyzz')
        self.assertEqual(l2.referenceMap().id(), 'zzyyzz')
Example #8
0
    def testLayouts(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)
        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout3')

        manager.addLayout(layout)
        self.assertEqual(manager.layouts(), [layout])
        manager.addLayout(layout2)
        self.assertEqual(set(manager.layouts()), {layout, layout2})
        manager.addLayout(layout3)
        self.assertEqual(set(manager.layouts()), {layout, layout2, layout3})
    def test_move_chart_in_layout(self):
        """
        Test moving charts in layout plot up and down
        """
        print('moving charts in layout plot up and down')

        # create project and layout
        project = QgsProject.instance()
        layout = QgsPrintLayout(project)
        layout_name = "PrintLayoutMovingUpDown"
        layout.initializeDefaults()
        layout.setName(layout_name)
        manager = project.layoutManager()
        self.assertEqual(True, manager.addLayout(layout))
        layout = manager.layoutByName(layout_name)
        layout_plot = PlotLayoutItem(layout)
        self.assertEqual(len(layout_plot.plot_settings), 1)
        # self.assertEqual(len(layout.items()), 0)
        layout.addLayoutItem(layout_plot)
        # self.assertEqual(len(layout.items()), 1)
        plot_dialog = PlotLayoutItemWidget(None, layout_plot)

        # add second plot
        plot_dialog.add_plot()
        self.assertEqual(len(layout_plot.plot_settings), 2)

        # edit first plot
        plot_dialog.setDockMode(True)
        plot_dialog.show_properties()
        plot_property_panel = plot_dialog.panel
        plot_property_panel.set_plot_type('violin')
        self.assertEqual(plot_property_panel.ptype, 'violin')
        plot_property_panel.acceptPanel()
        plot_property_panel.destroy()

        # edit second plot
        plot_dialog.plot_list.setCurrentRow(1)
        plot_dialog.show_properties()
        plot_property_panel = plot_dialog.panel
        plot_property_panel.set_plot_type('bar')
        self.assertEqual(plot_property_panel.ptype, 'bar')
        plot_property_panel.acceptPanel()
        plot_property_panel.destroy()

        # move up and down

        # cannot move up first item
        plot_dialog.plot_list.setCurrentRow(0)
        plot_dialog.move_up_plot()
        self.assertEqual(layout_plot.plot_settings[0].plot_type, 'violin')
        self.assertEqual(layout_plot.plot_settings[1].plot_type, 'bar')
        # move up second item
        plot_dialog.plot_list.setCurrentRow(1)
        plot_dialog.move_up_plot()
        self.assertEqual(layout_plot.plot_settings[0].plot_type, 'bar')
        self.assertEqual(layout_plot.plot_settings[1].plot_type, 'violin')

        # cannot move down second item
        plot_dialog.plot_list.setCurrentRow(1)
        plot_dialog.move_down_plot()
        self.assertEqual(layout_plot.plot_settings[0].plot_type, 'bar')
        self.assertEqual(layout_plot.plot_settings[1].plot_type, 'violin')
        # move down first item
        plot_dialog.plot_list.setCurrentRow(0)
        plot_dialog.move_down_plot()
        self.assertEqual(layout_plot.plot_settings[0].plot_type, 'violin')
        self.assertEqual(layout_plot.plot_settings[1].plot_type, 'bar')

        self.assertEqual(True, manager.removeLayout(layout))
Example #10
0
    def testRenameSignal(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)
        layout = QgsPrintLayout(project)
        layout.setName('c1')
        manager.addLayout(layout)
        layout2 = QgsPrintLayout(project)
        layout2.setName('c2')
        manager.addLayout(layout2)

        layout_renamed_spy = QSignalSpy(manager.layoutRenamed)
        layout.setName('d1')
        self.assertEqual(len(layout_renamed_spy), 1)
        # self.assertEqual(layout_renamed_spy[0][0], layout)
        self.assertEqual(layout_renamed_spy[0][1], 'd1')
        layout2.setName('d2')
        self.assertEqual(len(layout_renamed_spy), 2)
        # self.assertEqual(layout_renamed_spy[1][0], layout2)
        self.assertEqual(layout_renamed_spy[1][1], 'd2')
Example #11
0
    def testIteration(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setCoverageLayer(vector_layer)

        atlas_feature_changed_spy = QSignalSpy(atlas.featureChanged)
        context_changed_spy = QSignalSpy(l.reportContext().changed)

        self.assertTrue(atlas.beginRender())
        self.assertTrue(atlas.first())
        self.assertEqual(len(atlas_feature_changed_spy), 1)
        self.assertEqual(len(context_changed_spy), 1)
        self.assertEqual(atlas.currentFeatureNumber(), 0)
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        self.assertEqual(l.reportContext().layer(), vector_layer)
        f1 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 2)
        self.assertEqual(len(context_changed_spy), 2)
        self.assertEqual(atlas.currentFeatureNumber(), 1)
        self.assertEqual(l.reportContext().feature()[4], 'Bretagne')
        f2 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 3)
        self.assertEqual(len(context_changed_spy), 3)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')
        f3 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 4)
        self.assertEqual(len(context_changed_spy), 4)
        self.assertEqual(atlas.currentFeatureNumber(), 3)
        self.assertEqual(l.reportContext().feature()[4], 'Centre')
        f4 = l.reportContext().feature()

        self.assertFalse(atlas.next())
        self.assertTrue(atlas.seekTo(2))
        self.assertEqual(len(atlas_feature_changed_spy), 5)
        self.assertEqual(len(context_changed_spy), 5)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')

        self.assertTrue(atlas.last())
        self.assertEqual(len(atlas_feature_changed_spy), 6)
        self.assertEqual(len(context_changed_spy), 6)
        self.assertEqual(atlas.currentFeatureNumber(), 3)
        self.assertEqual(l.reportContext().feature()[4], 'Centre')

        self.assertTrue(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 7)
        self.assertEqual(len(context_changed_spy), 7)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')

        self.assertTrue(atlas.previous())
        self.assertTrue(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 9)
        self.assertFalse(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 9)

        self.assertTrue(atlas.endRender())
        self.assertEqual(len(atlas_feature_changed_spy), 10)

        self.assertTrue(atlas.seekTo(f1))
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        self.assertTrue(atlas.seekTo(f4))
        self.assertEqual(l.reportContext().feature()[4], 'Centre')
        self.assertTrue(atlas.seekTo(f3))
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')
        self.assertTrue(atlas.seekTo(f2))
        self.assertEqual(l.reportContext().feature()[4], 'Bretagne')
        self.assertFalse(atlas.seekTo(QgsFeature(5)))
    def testModel(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)
        model = QgsLayoutManagerModel(manager)
        self.assertEqual(model.rowCount(QModelIndex()), 0)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
        self.assertEqual(model.indexFromLayout(None), QModelIndex())

        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        self.assertEqual(model.indexFromLayout(layout), QModelIndex())
        self.assertTrue(manager.addLayout(layout))

        self.assertEqual(model.rowCount(QModelIndex()), 1)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'test layout')
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout)
        self.assertEqual(model.indexFromLayout(layout),
                         model.index(0, 0, QModelIndex()))
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(1, 0, QModelIndex())), None)

        layout.setName('test Layout')
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'test Layout')

        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        self.assertTrue(manager.addLayout(layout2))

        self.assertEqual(model.rowCount(QModelIndex()), 2)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'test Layout')
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout)
        self.assertEqual(model.indexFromLayout(layout),
                         model.index(0, 0, QModelIndex()))
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'test layout2')
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)
        self.assertEqual(
            model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout2)
        self.assertEqual(model.indexFromLayout(layout2),
                         model.index(1, 0, QModelIndex()))

        manager.removeLayout(layout)
        self.assertEqual(model.rowCount(QModelIndex()), 1)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'test layout2')
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout2)
        self.assertEqual(
            model.layoutFromIndex(model.index(1, 0, QModelIndex())), None)
        self.assertEqual(model.indexFromLayout(layout2),
                         model.index(0, 0, QModelIndex()))

        manager.clear()
        self.assertEqual(model.rowCount(QModelIndex()), 0)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)

        # with empty row
        model.setAllowEmptyLayout(True)
        self.assertEqual(model.rowCount(QModelIndex()), 1)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)

        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        self.assertTrue(manager.addLayout(layout))
        self.assertEqual(model.rowCount(QModelIndex()), 2)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'test layout')
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            model.data(model.index(2, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(2, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout)
        self.assertEqual(model.indexFromLayout(layout),
                         model.index(1, 0, QModelIndex()))

        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        self.assertTrue(manager.addLayout(layout2))
        self.assertEqual(model.rowCount(QModelIndex()), 3)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'test layout')
        self.assertEqual(
            model.data(model.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            model.data(model.index(2, 0, QModelIndex()), Qt.DisplayRole),
            'test layout2')
        self.assertEqual(
            model.data(model.index(2, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout)
        self.assertEqual(
            model.layoutFromIndex(model.index(2, 0, QModelIndex())), layout2)
        self.assertEqual(model.indexFromLayout(layout),
                         model.index(1, 0, QModelIndex()))
        self.assertEqual(model.indexFromLayout(layout2),
                         model.index(2, 0, QModelIndex()))

        manager.clear()
        self.assertEqual(model.rowCount(QModelIndex()), 1)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            model.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
    def testProxyModel(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)
        model = QgsLayoutManagerModel(manager)
        proxy = QgsLayoutManagerProxyModel()
        proxy.setSourceModel(model)
        self.assertEqual(proxy.rowCount(QModelIndex()), 0)
        self.assertEqual(
            proxy.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(model.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)

        layout = QgsPrintLayout(project)
        layout.setName('ccc')
        self.assertTrue(manager.addLayout(layout))

        self.assertEqual(proxy.rowCount(QModelIndex()), 1)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'ccc')
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)

        layout2 = QgsPrintLayout(project)
        layout2.setName('bbb')
        self.assertTrue(manager.addLayout(layout2))
        self.assertEqual(proxy.rowCount(QModelIndex()), 2)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'bbb')
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'ccc')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)

        layout.setName('aaa')
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole),
            'aaa')
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'bbb')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)

        model.setAllowEmptyLayout(True)
        self.assertEqual(proxy.rowCount(QModelIndex()), 3)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'aaa')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole),
            'bbb')
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)

        r = QgsReport(project)
        r.setName('ddd')
        manager.addLayout(r)
        self.assertEqual(proxy.rowCount(QModelIndex()), 4)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'aaa')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole),
            'bbb')
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)
        self.assertEqual(
            proxy.data(proxy.index(3, 0, QModelIndex()), Qt.DisplayRole),
            'ddd')
        self.assertEqual(
            proxy.data(proxy.index(3, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), r)

        proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts)
        self.assertEqual(proxy.filters(),
                         QgsLayoutManagerProxyModel.FilterPrintLayouts)
        self.assertEqual(proxy.rowCount(QModelIndex()), 3)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'aaa')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout)
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole),
            'bbb')
        self.assertEqual(
            proxy.data(proxy.index(2, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), layout2)

        proxy.setFilters(QgsLayoutManagerProxyModel.FilterReports)
        self.assertEqual(proxy.filters(),
                         QgsLayoutManagerProxyModel.FilterReports)
        self.assertEqual(proxy.rowCount(QModelIndex()), 2)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
        self.assertEqual(
            proxy.data(proxy.index(0, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), None)
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole),
            'ddd')
        self.assertEqual(
            proxy.data(proxy.index(1, 0, QModelIndex()),
                       QgsLayoutManagerModel.LayoutRole), r)

        proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts
                         | QgsLayoutManagerProxyModel.FilterReports)
        self.assertEqual(
            proxy.filters(), QgsLayoutManagerProxyModel.FilterPrintLayouts
            | QgsLayoutManagerProxyModel.FilterReports)
        self.assertEqual(proxy.rowCount(QModelIndex()), 4)
Example #14
0
    def run(self,
            templatePath,
            entityFieldName,
            entityFieldValue,
            outputMode,
            filePath=None,
            dataFields=None,
            fileExtension=None,
            data_source=None):
        """
        :param templatePath: The file path to the user-defined template.
        :param entityFieldName: The name of the column for the specified entity which
        must exist in the data source view or table.
        :param entityFieldValue: The value for filtering the records in the data source
        view or table.
        :param outputMode: Whether the output composition should be an image or PDF.
        :param filePath: The output file where the composition will be written to. Applies
        to single mode output generation.
        :param dataFields: List containing the field names whose values will be used to name the files.
        This is used in multiple mode configuration.
        :param fileExtension: The output file format. Used in multiple mode configuration.
        :param data_source: Name of the data source table or view whose
        row values will be used to name output files if the options has been
        specified by the user.
        """
        if dataFields is None:
            dataFields = []

        if fileExtension is None:
            fileExtension = ''

        if data_source is None:
            data_source = ''

        templateFile = QFile(templatePath)

        if not templateFile.open(QIODevice.ReadOnly):
            return False, QApplication.translate("DocumentGenerator",
                                                 "Cannot read template file.")

        templateDoc = QDomDocument()

        if templateDoc.setContent(templateFile):
            composerDS = ComposerDataSource.create(templateDoc)
            spatialFieldsConfig = SpatialFieldsConfiguration.create(
                templateDoc)
            composerDS.setSpatialFieldsConfig(spatialFieldsConfig)

            # Check if data source exists and return if it doesn't
            if not self.data_source_exists(composerDS):
                msg = QApplication.translate(
                    "DocumentGenerator",
                    "'{0}' data source does not exist in the database."
                    "\nPlease contact your database "
                    "administrator.".format(composerDS.name()))
                return False, msg

            # Set file name value formatter
            self._file_name_value_formatter = EntityValueFormatter(
                name=data_source)

            # Register field names to be used for file naming
            self._file_name_value_formatter.register_columns(dataFields)

            # TODO: Need to automatically register custom configuration collections
            # Photo config collection
            ph_config_collection = PhotoConfigurationCollection.create(
                templateDoc)

            # Table configuration collection
            table_config_collection = TableConfigurationCollection.create(
                templateDoc)

            # Create chart configuration collection object
            chart_config_collection = ChartConfigurationCollection.create(
                templateDoc)

            # Create QR code configuration collection object
            qrc_config_collection = QRCodeConfigurationCollection.create(
                templateDoc)

            # Load the layers required by the table composer items
            self._table_mem_layers = load_table_layers(table_config_collection)

            entityFieldName = self.format_entity_field_name(
                composerDS.name(), data_source)

            # Execute query
            dsTable, records = self._exec_query(composerDS.name(),
                                                entityFieldName,
                                                entityFieldValue)

            if records is None or len(records) == 0:
                return False, QApplication.translate(
                    "DocumentGenerator", "No matching records in the database")
            """
            Iterate through records where a single file output will be generated for each matching record.
            """

            for rec in records:
                composition = QgsPrintLayout(self._map_settings)
                context = QgsReadWriteContext()
                composition.loadFromTemplate(templateDoc, context)
                ref_layer = None
                # Set value of composer items based on the corresponding db values
                for composerId in composerDS.dataFieldMappings().reverse:
                    # Use composer item id since the uuid is stripped off
                    composerItem = composition.itemById(composerId)
                    if composerItem is not None:
                        fieldName = composerDS.dataFieldName(composerId)
                        fieldValue = getattr(rec, fieldName)
                        self._composeritem_value_handler(
                            composerItem, fieldValue)

                # Extract photo information
                self._extract_photo_info(composition, ph_config_collection,
                                         rec)

                # Set table item values based on configuration information
                self._set_table_data(composition, table_config_collection, rec)

                # Refresh non-custom map composer items
                self._refresh_composer_maps(
                    composition,
                    list(spatialFieldsConfig.spatialFieldsMapping().keys()))

                # Set use fixed scale to false i.e. relative zoom
                use_fixed_scale = False

                # Create memory layers for spatial features and add them to the map
                for mapId, spfmList in spatialFieldsConfig.spatialFieldsMapping(
                ).items():

                    map_item = composition.itemById(mapId)

                    if map_item is not None:
                        # Clear any previous map memory layer
                        # self.clear_temporary_map_layers()
                        for spfm in spfmList:
                            # Use the value of the label field to name the layer
                            lbl_field = spfm.labelField()
                            spatial_field = spfm.spatialField()

                            if not spatial_field:
                                continue

                            if lbl_field:
                                if hasattr(rec, spfm.labelField()):
                                    layerName = getattr(rec, spfm.labelField())
                                else:
                                    layerName = self._random_feature_layer_name(
                                        spatial_field)
                            else:
                                layerName = self._random_feature_layer_name(
                                    spatial_field)

                            # Extract the geometry using geoalchemy spatial capabilities
                            geom_value = getattr(rec, spatial_field)
                            if geom_value is None:
                                continue

                            geom_func = geom_value.ST_AsText()
                            geomWKT = self._dbSession.scalar(geom_func)

                            # Get geometry type
                            geom_type, srid = geometryType(
                                composerDS.name(), spatial_field)

                            # Create reference layer with feature
                            ref_layer = self._build_vector_layer(
                                layerName, geom_type, srid)

                            if ref_layer is None or not ref_layer.isValid():
                                continue
                            # Add feature
                            bbox = self._add_feature_to_layer(
                                ref_layer, geomWKT)

                            zoom_type = spfm.zoom_type
                            # Only scale the extents if zoom type is relative
                            if zoom_type == 'RELATIVE':
                                bbox.scale(spfm.zoomLevel())

                            # Workaround for zooming to single point extent
                            if ref_layer.wkbType() == QgsWkbTypes.Point:
                                canvas_extent = self._iface.mapCanvas(
                                ).fullExtent()
                                cnt_pnt = bbox.center()
                                canvas_extent.scale(1.0 / 32, cnt_pnt)
                                bbox = canvas_extent

                            # Style layer based on the spatial field mapping symbol layer
                            symbol_layer = spfm.symbolLayer()
                            if symbol_layer is not None:
                                ref_layer.renderer().symbols(
                                )[0].changeSymbolLayer(0, spfm.symbolLayer())
                            '''
                            Add layer to map and ensure its always added at the top
                            '''
                            self.map_registry.addMapLayer(ref_layer)
                            self._iface.mapCanvas().setExtent(bbox)

                            # Set scale if type is FIXED
                            if zoom_type == 'FIXED':
                                self._iface.mapCanvas().zoomScale(
                                    spfm.zoomLevel())
                                use_fixed_scale = True

                            self._iface.mapCanvas().refresh()
                            # Add layer to map memory layer list
                            self._map_memory_layers.append(ref_layer.id())
                            self._hide_layer(ref_layer)
                        '''
                        Use root layer tree to get the correct ordering of layers
                        in the legend
                        '''
                        self._refresh_map_item(map_item, use_fixed_scale)

                # Extract chart information and generate chart
                self._generate_charts(composition, chart_config_collection,
                                      rec)

                # Extract QR code information in order to generate QR codes
                self._generate_qr_codes(composition, qrc_config_collection,
                                        rec)

                # Build output path and generate composition
                if filePath is not None and len(dataFields) == 0:
                    self._write_output(composition, outputMode, filePath)

                elif filePath is None and len(dataFields) > 0:
                    entityFieldName = 'id'
                    docFileName = self._build_file_name(
                        data_source, entityFieldName, entityFieldValue,
                        dataFields, fileExtension)

                    # Replace unsupported characters in Windows file naming
                    docFileName = docFileName.replace('/', '_').replace(
                        '\\', '_').replace(':', '_').strip('*?"<>|')

                    if not docFileName:
                        return (
                            False,
                            QApplication.translate(
                                "DocumentGenerator",
                                "File name could not be generated from the data fields."
                            ))

                    outputDir = self._composer_output_path()
                    if outputDir is None:
                        return (
                            False,
                            QApplication.translate(
                                "DocumentGenerator",
                                "System could not read the location of the output directory in the registry."
                            ))

                    qDir = QDir()
                    if not qDir.exists(outputDir):
                        return (False,
                                QApplication.translate(
                                    "DocumentGenerator",
                                    "Output directory does not exist"))

                    absDocPath = "{0}/{1}".format(outputDir, docFileName)
                    self._write_output(composition, outputMode, absDocPath)

            return True, "Success"

        return False, "Document composition could not be generated"
Example #15
0
def set_image(layout: QgsPrintLayout, item_id: str, fp: str):
  '''Adds references to the layout image items'''
  logging.info(f'setting image source for {item_id} using {fp}')
  item = layout.itemById(item_id)
  item.setPicturePath(fp)
Example #16
0
def hide_item(layout: QgsPrintLayout, item_id: str):
  '''Hides an item by item_id from Layout'''
  logging.info(f'hiding {item_id} in layout')
  item = layout.itemById(item_id)
  item.setVisibility(False)
    def createLayout(self):
        #print('createLayout()')

        templateFile = self.createLayoutDialog.comboBoxTemplate.currentText()
        templateQpt = os.path.join(self.dataPath, templateFile)

        layoutName = self.createLayoutDialog.lineEditName.text()

        project = QgsProject.instance()
        layoutManager = project.layoutManager()

        oldLayout = layoutManager.layoutByName(layoutName)
        if oldLayout is not None:
            #print(f'removing: {oldLayout}')
            layoutManager.removeLayout(oldLayout)

        # create new layout
        layout = QgsPrintLayout(project)

        # load layout template
        fl = QFile(templateQpt)
        doc = QDomDocument()
        doc.setContent(fl)
        layout.loadFromTemplate(doc, QgsReadWriteContext())

        # set name
        layout.setName(layoutName)

        # set map properties
        map = self.getItemById(layout, 'RC_map')
        if map is not None:
            #print(map.crs().description())
            map.zoomToExtent(self.iface.mapCanvas().extent())

        # set default label values
        for configurationItem in simsLayoutConfiguration:
            label = self.getItemById(layout, configurationItem['code'])
            if label is not None and isinstance(label, QgsLayoutItemLabel):
                if configurationItem['default'] is not None:
                    label.setText(configurationItem['default'])

        # set overview map
        map = self.getItemById(layout, 'RC_overview')
        if map is not None:
            worldLayer = self.addWorldLayer()
            map.setFollowVisibilityPreset(False)
            map.setKeepLayerSet(True)
            map.setLayers([worldLayer])

            overviewCrs = QgsCoordinateReferenceSystem('EPSG:54030')
            map.setCrs(overviewCrs)

            extent = worldLayer.extent()
            layerCrs = worldLayer.sourceCrs()
            if not layerCrs == overviewCrs:
                transform = QgsCoordinateTransform(layerCrs, overviewCrs, QgsProject.instance())
                extent = transform.transformBoundingBox(extent)
            map.zoomToExtent(extent)

            root = QgsProject.instance().layerTreeRoot()
            rl = root.findLayer(worldLayer.id())
            rl.setItemVisibilityChecked(False)

        # set disclamer
        languageChoice = self.createLayoutDialog.comboBoxLanguage.currentText()

        label = self.getItemById(layout, 'RC_disclaimer')
        if label is not None:
            label.setText(simsDisclamers[languageChoice])

        # set title
        label = self.getItemById(layout, 'RC_title')
        if label is not None:
            label.setText(self.createLayoutDialog.lineEditName.text())

        # set Copyright
        # possibly dynamically: [%'© SIMS '  || year(now())%]
        '''
        label = self.getItemById(layout, 'COPYRIGHT')
        if label is not None:
            print(label)
            label.setText('© SIMS {0}'.format(datetime.now().year))

        # set filename
        label = self.getItemById(layout, 'FILENAME')
        if label is not None:
            print(label)
            filename = QgsProject.instance().fileName()
            if filename == '':
                filename = 'filename unknown, project not saved'
            label.setText(filename)
        '''

        # set NS logo
        picture = self.getItemById(layout, 'RC_logo1')
        if picture is not None:
            logoChoice = self.createLayoutDialog.comboBoxNsLogo.currentText()
            logoSvg = os.path.join(self.dataPath, 'logos', logoChoice)
            picture.setPicturePath(self.toEmbeddable(logoSvg), QgsLayoutItemPicture.FormatSVG)

        # set IFRC logo
        picture = self.getItemById(layout, 'RC_logo2')
        if picture is not None:
            logoSvg = os.path.join(self.dataPath, 'img', 'IFRC_logo_English_horizontal_no_gaps.svg')
            picture.setPicturePath(self.toEmbeddable(logoSvg), QgsLayoutItemPicture.FormatSVG)

        # set date
        label = self.getItemById(layout, 'RC_date')
        if label is not None:
            now = datetime.now()
            month = simsMonths[languageChoice][now.month]
            label.setText(now.strftime(f'%d {month} %Y'))

        # set North Arrow
        picture = self.getItemById(layout, 'RC_northarrow')
        if picture is not None:
            logoSvg = os.path.join(QgsApplication.pkgDataPath(), 'svg', 'arrows', 'NorthArrow_02.svg')
            picture.setPicturePath(self.toEmbeddable(logoSvg), QgsLayoutItemPicture.FormatSVG)

        # clear default label values

        # add to project and open designer window
        layoutManager.addLayout(layout)
        designer = self.iface.openLayoutDesigner(layout)
Example #18
0
    def testLayoutsByName(self):
        project = QgsProject()
        manager = QgsLayoutManager(project)

        # add a bunch of layouts
        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout3')

        manager.addLayout(layout)
        manager.addLayout(layout2)
        manager.addLayout(layout3)

        self.assertFalse(manager.layoutByName('asdf'))
        self.assertEqual(manager.layoutByName('test layout'), layout)
        self.assertEqual(manager.layoutByName('test layout2'), layout2)
        self.assertEqual(manager.layoutByName('test layout3'), layout3)
Example #19
0
    def testReadWriteXml(self):
        """
        Test reading and writing layout manager state to XML
        """
        project = QgsProject()
        manager = QgsLayoutManager(project)

        # add a bunch of layouts
        layout = QgsPrintLayout(project)
        layout.setName('test layout')
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout3')

        manager.addLayout(layout)
        manager.addLayout(layout2)
        manager.addLayout(layout3)

        # save to xml
        doc = QDomDocument("testdoc")
        elem = manager.writeXml(doc)
        doc.appendChild(elem)

        # restore from xml
        project2 = QgsProject()
        manager2 = QgsLayoutManager(project2)
        self.assertTrue(manager2.readXml(elem, doc))

        self.assertEqual(len(manager2.layouts()), 3)
        names = [c.name() for c in manager2.layouts()]
        self.assertCountEqual(names, ['test layout', 'test layout2', 'test layout3'])
Example #20
0
class TestQgsLayoutAtlas(unittest.TestCase):

    def setUp(self):
        self.report = "<h1>Python QgsLayoutAtlas Tests</h1>\n"

    def tearDown(self):
        report_file_path = "%s/qgistest.html" % QDir.tempPath()
        with open(report_file_path, 'a') as report_file:
            report_file.write(self.report)

    def testCase(self):
        self.TEST_DATA_DIR = unitTestDataPath()
        tmppath = tempfile.mkdtemp()
        for file in glob.glob(os.path.join(self.TEST_DATA_DIR, 'france_parts.*')):
            shutil.copy(os.path.join(self.TEST_DATA_DIR, file), tmppath)
        vectorFileInfo = QFileInfo(tmppath + "/france_parts.shp")
        mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")

        QgsProject.instance().addMapLayers([mVectorLayer])
        self.layers = [mVectorLayer]

        # create layout with layout map

        # select epsg:2154
        crs = QgsCoordinateReferenceSystem()
        crs.createFromSrid(2154)
        QgsProject.instance().setCrs(crs)

        self.layout = QgsPrintLayout(QgsProject.instance())
        self.layout.initializeDefaults()

        # fix the renderer, fill with green
        props = {"color": "0,127,0", 'outline_color': 'black'}
        fillSymbol = QgsFillSymbol.createSimple(props)
        renderer = QgsSingleSymbolRenderer(fillSymbol)
        mVectorLayer.setRenderer(renderer)

        # the atlas map
        self.atlas_map = QgsLayoutItemMap(self.layout)
        self.atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130))
        self.atlas_map.setFrameEnabled(True)
        self.atlas_map.setLayers([mVectorLayer])
        self.layout.addLayoutItem(self.atlas_map)

        # the atlas
        self.atlas = self.layout.atlas()
        self.atlas.setCoverageLayer(mVectorLayer)
        self.atlas.setEnabled(True)

        # an overview
        self.overview = QgsLayoutItemMap(self.layout)
        self.overview.attemptSetSceneRect(QRectF(180, 20, 50, 50))
        self.overview.setFrameEnabled(True)
        self.overview.overview().setLinkedMap(self.atlas_map)
        self.overview.setLayers([mVectorLayer])
        self.layout.addLayoutItem(self.overview)
        nextent = QgsRectangle(49670.718, 6415139.086, 699672.519, 7065140.887)
        self.overview.setExtent(nextent)

        # set the fill symbol of the overview map
        props2 = {"color": "127,0,0,127", 'outline_color': 'black'}
        fillSymbol2 = QgsFillSymbol.createSimple(props2)
        self.overview.overview().setFrameSymbol(fillSymbol2)

        # header label
        self.mLabel1 = QgsLayoutItemLabel(self.layout)
        self.layout.addLayoutItem(self.mLabel1)
        self.mLabel1.setText("[% \"NAME_1\" %] area")
        self.mLabel1.setFont(QgsFontUtils.getStandardTestFont())
        self.mLabel1.adjustSizeToText()
        self.mLabel1.attemptSetSceneRect(QRectF(150, 5, 60, 15))
        self.mLabel1.setMarginX(1)
        self.mLabel1.setMarginY(1)

        # feature number label
        self.mLabel2 = QgsLayoutItemLabel(self.layout)
        self.layout.addLayoutItem(self.mLabel2)
        self.mLabel2.setText("# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]")
        self.mLabel2.setFont(QgsFontUtils.getStandardTestFont())
        self.mLabel2.adjustSizeToText()
        self.mLabel2.attemptSetSceneRect(QRectF(150, 200, 60, 15))
        self.mLabel2.setMarginX(1)
        self.mLabel2.setMarginY(1)

        self.filename_test()
        self.autoscale_render_test()
        self.fixedscale_render_test()
        self.predefinedscales_render_test()
        self.hidden_render_test()
        self.legend_test()
        self.rotation_test()

        shutil.rmtree(tmppath, True)

    def testReadWriteXml(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setHideCoverage(True)
        atlas.setFilenameExpression('filename exp')
        atlas.setCoverageLayer(vector_layer)
        atlas.setPageNameExpression('page name')
        atlas.setSortFeatures(True)
        atlas.setSortAscending(False)
        atlas.setSortExpression('sort exp')
        atlas.setFilterFeatures(True)
        atlas.setFilterExpression('filter exp')

        doc = QDomDocument("testdoc")
        elem = l.writeXml(doc, QgsReadWriteContext())

        l2 = QgsPrintLayout(p)
        self.assertTrue(l2.readXml(elem, doc, QgsReadWriteContext()))
        atlas2 = l2.atlas()
        self.assertTrue(atlas2.enabled())
        self.assertTrue(atlas2.hideCoverage())
        self.assertEqual(atlas2.filenameExpression(), 'filename exp')
        self.assertEqual(atlas2.coverageLayer(), vector_layer)
        self.assertEqual(atlas2.pageNameExpression(), 'page name')
        self.assertTrue(atlas2.sortFeatures())
        self.assertFalse(atlas2.sortAscending())
        self.assertEqual(atlas2.sortExpression(), 'sort exp')
        self.assertTrue(atlas2.filterFeatures())
        self.assertEqual(atlas2.filterExpression(), 'filter exp')

    def testIteration(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setCoverageLayer(vector_layer)

        atlas_feature_changed_spy = QSignalSpy(atlas.featureChanged)
        context_changed_spy = QSignalSpy(l.reportContext().changed)

        self.assertTrue(atlas.beginRender())
        self.assertTrue(atlas.first())
        self.assertEqual(len(atlas_feature_changed_spy), 1)
        self.assertEqual(len(context_changed_spy), 1)
        self.assertEqual(atlas.currentFeatureNumber(), 0)
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        self.assertEqual(l.reportContext().layer(), vector_layer)
        f1 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 2)
        self.assertEqual(len(context_changed_spy), 2)
        self.assertEqual(atlas.currentFeatureNumber(), 1)
        self.assertEqual(l.reportContext().feature()[4], 'Bretagne')
        f2 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 3)
        self.assertEqual(len(context_changed_spy), 3)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')
        f3 = l.reportContext().feature()

        self.assertTrue(atlas.next())
        self.assertEqual(len(atlas_feature_changed_spy), 4)
        self.assertEqual(len(context_changed_spy), 4)
        self.assertEqual(atlas.currentFeatureNumber(), 3)
        self.assertEqual(l.reportContext().feature()[4], 'Centre')
        f4 = l.reportContext().feature()

        self.assertFalse(atlas.next())
        self.assertTrue(atlas.seekTo(2))
        self.assertEqual(len(atlas_feature_changed_spy), 5)
        self.assertEqual(len(context_changed_spy), 5)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')

        self.assertTrue(atlas.last())
        self.assertEqual(len(atlas_feature_changed_spy), 6)
        self.assertEqual(len(context_changed_spy), 6)
        self.assertEqual(atlas.currentFeatureNumber(), 3)
        self.assertEqual(l.reportContext().feature()[4], 'Centre')

        self.assertTrue(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 7)
        self.assertEqual(len(context_changed_spy), 7)
        self.assertEqual(atlas.currentFeatureNumber(), 2)
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')

        self.assertTrue(atlas.previous())
        self.assertTrue(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 9)
        self.assertFalse(atlas.previous())
        self.assertEqual(len(atlas_feature_changed_spy), 9)

        self.assertTrue(atlas.endRender())
        self.assertEqual(len(atlas_feature_changed_spy), 10)

        self.assertTrue(atlas.seekTo(f1))
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        self.assertTrue(atlas.seekTo(f4))
        self.assertEqual(l.reportContext().feature()[4], 'Centre')
        self.assertTrue(atlas.seekTo(f3))
        self.assertEqual(l.reportContext().feature()[4], 'Pays de la Loire')
        self.assertTrue(atlas.seekTo(f2))
        self.assertEqual(l.reportContext().feature()[4], 'Bretagne')
        self.assertFalse(atlas.seekTo(QgsFeature(5)))

    def testUpdateFeature(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setCoverageLayer(vector_layer)

        self.assertTrue(atlas.beginRender())
        self.assertTrue(atlas.first())
        self.assertEqual(atlas.currentFeatureNumber(), 0)
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        self.assertEqual(l.reportContext().layer(), vector_layer)

        vector_layer.startEditing()
        self.assertTrue(vector_layer.changeAttributeValue(l.reportContext().feature().id(), 4, 'Nah, Canberra mate!'))
        self.assertEqual(l.reportContext().feature()[4], 'Basse-Normandie')
        l.atlas().refreshCurrentFeature()
        self.assertEqual(l.reportContext().feature()[4], 'Nah, Canberra mate!')
        vector_layer.rollBack()

    def testFileName(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setCoverageLayer(vector_layer)
        atlas.setFilenameExpression("'output_' || \"NAME_1\"")

        self.assertTrue(atlas.beginRender())
        self.assertEqual(atlas.count(), 4)
        atlas.first()
        self.assertEqual(atlas.currentFilename(), 'output_Basse-Normandie')
        self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Basse-Normandie.png')
        self.assertEqual(atlas.filePath('/tmp/output/', '.png'), '/tmp/output/output_Basse-Normandie.png')
        self.assertEqual(atlas.filePath('/tmp/output/', 'svg'), '/tmp/output/output_Basse-Normandie.svg')

        atlas.next()
        self.assertEqual(atlas.currentFilename(), 'output_Bretagne')
        self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Bretagne.png')
        atlas.next()
        self.assertEqual(atlas.currentFilename(), 'output_Pays de la Loire')
        self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Pays de la Loire.png')
        atlas.next()
        self.assertEqual(atlas.currentFilename(), 'output_Centre')
        self.assertEqual(atlas.filePath('/tmp/output/', 'png'), '/tmp/output/output_Centre.png')

        # try changing expression, filename should be updated instantly
        atlas.setFilenameExpression("'export_' || \"NAME_1\"")
        self.assertEqual(atlas.currentFilename(), 'export_Centre')

        atlas.endRender()

    def testNameForPage(self):
        p = QgsProject()
        vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
        vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
        self.assertTrue(vector_layer.isValid())
        p.addMapLayer(vector_layer)

        l = QgsPrintLayout(p)
        atlas = l.atlas()
        atlas.setEnabled(True)
        atlas.setCoverageLayer(vector_layer)
        atlas.setPageNameExpression("\"NAME_1\"")

        self.assertTrue(atlas.beginRender())
        self.assertEqual(atlas.nameForPage(0), 'Basse-Normandie')
        self.assertEqual(atlas.nameForPage(1), 'Bretagne')
        self.assertEqual(atlas.nameForPage(2), 'Pays de la Loire')
        self.assertEqual(atlas.nameForPage(3), 'Centre')

    def filename_test(self):
        self.atlas.setFilenameExpression("'output_' || @atlas_featurenumber")
        self.atlas.beginRender()
        for i in range(0, self.atlas.count()):
            self.atlas.seekTo(i)
            expected = "output_%d" % (i + 1)
            self.assertEqual(self.atlas.currentFilename(), expected)
        self.atlas.endRender()

        # using feature attribute (refs https://issues.qgis.org/issues/19552)

        self.atlas.setFilenameExpression("'output_' || attribute(@atlas_feature,'NAME_1')")
        expected = ['output_Basse-Normandie',
                    'output_Bretagne',
                    'output_Pays de la Loire',
                    'output_Centre']
        self.atlas.beginRender()
        for i in range(0, self.atlas.count()):
            self.atlas.seekTo(i)
            self.assertEqual(self.atlas.currentFilename(), expected[i])
        self.atlas.endRender()

    def autoscale_render_test(self):
        self.atlas_map.setExtent(
            QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338))

        self.atlas_map.setAtlasDriven(True)
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto)
        self.atlas_map.setAtlasMargin(0.10)

        self.atlas.beginRender()

        for i in range(0, 2):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_autoscale%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

        self.atlas_map.setAtlasDriven(False)
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed)
        self.atlas_map.setAtlasMargin(0)

    def fixedscale_render_test(self):
        self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620))
        self.atlas_map.setAtlasDriven(True)
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed)

        self.atlas.beginRender()

        for i in range(0, 2):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_fixedscale%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

    def predefinedscales_render_test(self):
        self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620))
        self.atlas_map.setAtlasDriven(True)
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Predefined)

        scales = [1800000, 5000000]
        self.layout.reportContext().setPredefinedScales(scales)
        for i, s in enumerate(self.layout.reportContext().predefinedScales()):
            self.assertEqual(s, scales[i])

        self.atlas.beginRender()

        for i in range(0, 2):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_predefinedscales%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

    def hidden_render_test(self):
        self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620))
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed)
        self.atlas.setHideCoverage(True)

        self.atlas.beginRender()

        for i in range(0, 2):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_hiding%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

        self.atlas.setHideCoverage(False)

    def sorting_render_test(self):
        self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620))
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed)
        self.atlas.setHideCoverage(False)

        self.atlas.setSortFeatures(True)
        self.atlas.setSortKeyAttributeIndex(4)  # departement name
        self.atlas.setSortAscending(False)

        self.atlas.beginRender()

        for i in range(0, 2):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_sorting%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

    def filtering_render_test(self):
        self.atlas_map.setExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620))
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Fixed)
        self.atlas.setHideCoverage(False)

        self.atlas.setSortFeatures(False)

        self.atlas.setFilterFeatures(True)
        self.atlas.setFeatureFilter("substr(NAME_1,1,1)='P'")  # select only 'Pays de la loire'

        self.atlas.beginRender()

        for i in range(0, 1):
            self.atlas.seekTo(i)
            self.mLabel1.adjustSizeToText()

            checker = QgsLayoutChecker('atlas_filtering%d' % (i + 1), self.layout)
            checker.setControlPathPrefix("atlas")
            myTestResult, myMessage = checker.testLayout(0, 200)
            self.report += checker.report()

            self.assertTrue(myTestResult, myMessage)
        self.atlas.endRender()

    def legend_test(self):
        self.atlas_map.setAtlasDriven(True)
        self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto)
        self.atlas_map.setAtlasMargin(0.10)

        # add a point layer
        ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory")

        pr = ptLayer.dataProvider()
        f1 = QgsFeature(1)
        f1.initAttributes(2)
        f1.setAttribute(0, 1)
        f1.setAttribute(1, "Test label 1")
        f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-0.638, 48.954)))
        f2 = QgsFeature(2)
        f2.initAttributes(2)
        f2.setAttribute(0, 2)
        f2.setAttribute(1, "Test label 2")
        f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1.682, 48.550)))
        pr.addFeatures([f1, f2])

        # categorized symbology
        r = QgsCategorizedSymbolRenderer("attr", [QgsRendererCategory(1, QgsMarkerSymbol.createSimple({"color": "255,0,0", 'outline_color': 'black'}), "red"),
                                                  QgsRendererCategory(2, QgsMarkerSymbol.createSimple({"color": "0,0,255", 'outline_color': 'black'}), "blue")])
        ptLayer.setRenderer(r)

        QgsProject.instance().addMapLayer(ptLayer)

        # add the point layer to the map settings
        layers = self.layers
        layers = [ptLayer] + layers
        self.atlas_map.setLayers(layers)
        self.overview.setLayers(layers)

        # add a legend
        legend = QgsLayoutItemLegend(self.layout)
        legend.setTitle("Legend")
        legend.attemptMove(QgsLayoutPoint(200, 100))
        # sets the legend filter parameter
        legend.setLinkedMap(self.atlas_map)
        legend.setLegendFilterOutAtlas(True)
        self.layout.addLayoutItem(legend)

        self.atlas.beginRender()

        self.atlas.seekTo(0)
        self.mLabel1.adjustSizeToText()

        checker = QgsLayoutChecker('atlas_legend', self.layout)
        myTestResult, myMessage = checker.testLayout()
        self.report += checker.report()
        self.assertTrue(myTestResult, myMessage)

        self.atlas.endRender()

        # restore state
        self.atlas_map.setLayers([layers[1]])
        self.layout.removeLayoutItem(legend)
        QgsProject.instance().removeMapLayer(ptLayer.id())

    def rotation_test(self):
        # We will create a polygon layer with a rotated rectangle.
        # Then we will make it the object layer for the atlas,
        # rotate the map and test that the bounding rectangle
        # is smaller than the bounds without rotation.
        polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory')
        poly = QgsFeature(polygonLayer.fields())
        points = [(10, 15), (15, 10), (45, 40), (40, 45)]
        poly.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(x[0], x[1]) for x in points]]))
        polygonLayer.dataProvider().addFeatures([poly])
        QgsProject.instance().addMapLayer(polygonLayer)

        # Recreating the layout locally
        composition = QgsPrintLayout(QgsProject.instance())
        composition.initializeDefaults()

        # the atlas map
        atlasMap = QgsLayoutItemMap(composition)
        atlasMap.attemptSetSceneRect(QRectF(20, 20, 130, 130))
        atlasMap.setFrameEnabled(True)
        atlasMap.setLayers([polygonLayer])
        atlasMap.setExtent(QgsRectangle(0, 0, 100, 50))
        composition.addLayoutItem(atlasMap)

        # the atlas
        atlas = composition.atlas()
        atlas.setCoverageLayer(polygonLayer)
        atlas.setEnabled(True)

        atlasMap.setAtlasDriven(True)
        atlasMap.setAtlasScalingMode(QgsLayoutItemMap.Auto)
        atlasMap.setAtlasMargin(0.0)

        # Testing
        atlasMap.setMapRotation(0.0)
        atlas.beginRender()
        atlas.first()
        nonRotatedExtent = QgsRectangle(atlasMap.extent())

        atlasMap.setMapRotation(45.0)
        atlas.first()
        rotatedExtent = QgsRectangle(atlasMap.extent())

        self.assertLess(rotatedExtent.width(), nonRotatedExtent.width() * 0.9)
        self.assertLess(rotatedExtent.height(), nonRotatedExtent.height() * 0.9)

        QgsProject.instance().removeMapLayer(polygonLayer)
Example #21
0
    def testAddLayout(self):
        project = QgsProject()
        layout = QgsPrintLayout(project)
        layout.setName('test layout')

        manager = QgsLayoutManager(project)

        layout_about_to_be_added_spy = QSignalSpy(manager.layoutAboutToBeAdded)
        layout_added_spy = QSignalSpy(manager.layoutAdded)
        self.assertTrue(manager.addLayout(layout))
        self.assertEqual(len(layout_about_to_be_added_spy), 1)
        self.assertEqual(layout_about_to_be_added_spy[0][0], 'test layout')
        self.assertEqual(len(layout_added_spy), 1)
        self.assertEqual(layout_added_spy[0][0], 'test layout')

        # adding it again should fail
        self.assertFalse(manager.addLayout(layout))

        # try adding a second layout
        layout2 = QgsPrintLayout(project)
        layout2.setName('test layout2')
        self.assertTrue(manager.addLayout(layout2))
        self.assertEqual(len(layout_added_spy), 2)
        self.assertEqual(layout_about_to_be_added_spy[1][0], 'test layout2')
        self.assertEqual(len(layout_about_to_be_added_spy), 2)
        self.assertEqual(layout_added_spy[1][0], 'test layout2')

        # adding a layout with duplicate name should fail
        layout3 = QgsPrintLayout(project)
        layout3.setName('test layout2')
        self.assertFalse(manager.addLayout(layout3))
Example #22
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context
    qgis_composition_context = impact_report.qgis_composition_context

    # load composition object
    layout = QgsPrintLayout(QgsProject.instance())

    # load template
    main_template_folder = impact_report.metadata.template_folder

    # we do this condition in case custom template was found
    if component.template.startswith('../qgis-composer-templates/'):
        template_path = os.path.join(main_template_folder, component.template)
    else:
        template_path = component.template

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()

    # Replace
    for k, v in context.substitution_map.items():
        template_content = template_content.replace('[{}]'.format(k), v)

    document.setContent(template_content)

    rwcontext = QgsReadWriteContext()
    load_status = layout.loadFromTemplate(document, rwcontext)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = layout_item(layout, item_id, QgsLayoutItemPicture)
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = layout_item(layout, item_id, QgsLayoutItemHtml)
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsLayoutItemHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsLayoutItemHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    original_crs = impact_report.impact_function.crs
    destination_crs = qgis_composition_context.map_settings.destinationCrs()
    coord_transform = QgsCoordinateTransform(original_crs, destination_crs,
                                             QgsProject.instance())

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = [
            _layer for _layer in map_el.get('layers')
            if isinstance(_layer, QgsMapLayer)
        ]
        map_extent_option = map_el.get('extent')
        composer_map = layout_item(layout, item_id, QgsLayoutItemMap)

        for index, _layer in enumerate(layers):
            # we need to check whether the layer is registered or not
            registered_layer = (QgsProject.instance().mapLayer(_layer.id()))
            if registered_layer:
                if not registered_layer == _layer:
                    layers[index] = registered_layer
            else:
                QgsProject.instance().addMapLayer(_layer)
        """:type: qgis.core.QgsLayoutItemMap"""
        if composer_map:

            # Search for specified map extent in the template.
            min_x = composer_map.extent().xMinimum() if (
                impact_report.use_template_extent) else None
            min_y = composer_map.extent().yMinimum() if (
                impact_report.use_template_extent) else None
            max_x = composer_map.extent().xMaximum() if (
                impact_report.use_template_extent) else None
            max_y = composer_map.extent().yMaximum() if (
                impact_report.use_template_extent) else None

            composer_map.setKeepLayerSet(True)
            layer_set = [
                _layer for _layer in layers if isinstance(_layer, QgsMapLayer)
            ]
            composer_map.setLayers(layer_set)
            map_overview_extent = None
            if map_extent_option and isinstance(map_extent_option,
                                                QgsRectangle):
                # use provided map extent
                extent = coord_transform.transform(map_extent_option)
                for layer in layer_set:
                    layer_extent = coord_transform.transform(layer.extent())
                    if layer.name() == map_overview['id']:
                        map_overview_extent = layer_extent
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for layer in layer_set:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(layer.extent())
                    extent.combineExtentWith(layer_extent)
                    if layer.name() == map_overview['id']:
                        map_overview_extent = layer_extent

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = min_x or (center.x() - half_length - margin)
            max_x = max_x or (center.x() + half_length + margin)
            min_y = min_y or (center.y() - half_length - margin)
            max_y = max_y or (center.y() + half_length + margin)

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            if component.key == 'population-infographic' and (
                    map_overview_extent):
                square_extent = map_overview_extent

            composer_map.zoomToExtent(square_extent)
            composer_map.invalidateCache()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = [
            _layer for _layer in leg_el.get('layers')
            if isinstance(_layer, QgsMapLayer)
        ]
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = layout_item(layout, item_id, QgsLayoutItemLegend)
        """:type: qgis.core.QgsLayoutItemLegend"""
        if legend:
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 7:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 7 + 1)

            # set legend title
            if title is not None and not impact_report.legend_layers:
                legend.setTitle(title)

            # set legend
            root_group = legend.model().rootGroup()
            for _layer in layers:
                # we need to check whether the layer is registered or not
                registered_layer = (QgsProject.instance().mapLayer(
                    _layer.id()))
                if registered_layer:
                    if not registered_layer == _layer:
                        _layer = registered_layer
                else:
                    QgsProject.instance().addMapLayer(_layer)
                # used for customizations
                tree_layer = root_group.addLayer(_layer)
                if impact_report.legend_layers or (
                        not impact_report.multi_exposure_impact_function):
                    QgsLegendRenderer.setNodeLegendStyle(
                        tree_layer, QgsLegendStyle.Hidden)
            legend.adjustBoxSize()
            legend.updateFilterByMap(False)

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     layout, each_format,
                                                     component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(each_path, layout)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in list(output_format.items()):
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     layout, each_format,
                                                     component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(each_path, layout)
                component_output[key] = result_path
    elif (output_format
          in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(impact_report,
                                                 component_output_path, layout,
                                                 output_format, component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(component_output_path,
                                                      layout)
            component_output = result_path

    component.output = component_output

    return component.output
Example #23
0
    def testCase(self):
        self.TEST_DATA_DIR = unitTestDataPath()
        tmppath = tempfile.mkdtemp()
        for file in glob.glob(os.path.join(self.TEST_DATA_DIR, 'france_parts.*')):
            shutil.copy(os.path.join(self.TEST_DATA_DIR, file), tmppath)
        vectorFileInfo = QFileInfo(tmppath + "/france_parts.shp")
        mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")

        QgsProject.instance().addMapLayers([mVectorLayer])
        self.layers = [mVectorLayer]

        # create layout with layout map

        # select epsg:2154
        crs = QgsCoordinateReferenceSystem()
        crs.createFromSrid(2154)
        QgsProject.instance().setCrs(crs)

        self.layout = QgsPrintLayout(QgsProject.instance())
        self.layout.initializeDefaults()

        # fix the renderer, fill with green
        props = {"color": "0,127,0", 'outline_color': 'black'}
        fillSymbol = QgsFillSymbol.createSimple(props)
        renderer = QgsSingleSymbolRenderer(fillSymbol)
        mVectorLayer.setRenderer(renderer)

        # the atlas map
        self.atlas_map = QgsLayoutItemMap(self.layout)
        self.atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130))
        self.atlas_map.setFrameEnabled(True)
        self.atlas_map.setLayers([mVectorLayer])
        self.layout.addLayoutItem(self.atlas_map)

        # the atlas
        self.atlas = self.layout.atlas()
        self.atlas.setCoverageLayer(mVectorLayer)
        self.atlas.setEnabled(True)

        # an overview
        self.overview = QgsLayoutItemMap(self.layout)
        self.overview.attemptSetSceneRect(QRectF(180, 20, 50, 50))
        self.overview.setFrameEnabled(True)
        self.overview.overview().setLinkedMap(self.atlas_map)
        self.overview.setLayers([mVectorLayer])
        self.layout.addLayoutItem(self.overview)
        nextent = QgsRectangle(49670.718, 6415139.086, 699672.519, 7065140.887)
        self.overview.setExtent(nextent)

        # set the fill symbol of the overview map
        props2 = {"color": "127,0,0,127", 'outline_color': 'black'}
        fillSymbol2 = QgsFillSymbol.createSimple(props2)
        self.overview.overview().setFrameSymbol(fillSymbol2)

        # header label
        self.mLabel1 = QgsLayoutItemLabel(self.layout)
        self.layout.addLayoutItem(self.mLabel1)
        self.mLabel1.setText("[% \"NAME_1\" %] area")
        self.mLabel1.setFont(QgsFontUtils.getStandardTestFont())
        self.mLabel1.adjustSizeToText()
        self.mLabel1.attemptSetSceneRect(QRectF(150, 5, 60, 15))
        self.mLabel1.setMarginX(1)
        self.mLabel1.setMarginY(1)

        # feature number label
        self.mLabel2 = QgsLayoutItemLabel(self.layout)
        self.layout.addLayoutItem(self.mLabel2)
        self.mLabel2.setText("# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]")
        self.mLabel2.setFont(QgsFontUtils.getStandardTestFont())
        self.mLabel2.adjustSizeToText()
        self.mLabel2.attemptSetSceneRect(QRectF(150, 200, 60, 15))
        self.mLabel2.setMarginX(1)
        self.mLabel2.setMarginY(1)

        self.filename_test()
        self.autoscale_render_test()
        self.fixedscale_render_test()
        self.predefinedscales_render_test()
        self.hidden_render_test()
        self.legend_test()
        self.rotation_test()

        shutil.rmtree(tmppath, True)
    def test_read_write_project_with_layout(self):
        """
        Test saving/restoring dialog state of layout plot in project
        """
        print('read write project with layout test')

        # create project and layout
        project = QgsProject.instance()
        layout = QgsPrintLayout(project)
        layout_name = "PrintLayoutReadWrite"
        layout.initializeDefaults()
        layout.setName(layout_name)
        layout_plot = PlotLayoutItem(layout)
        layout_plot.setId('plot_item')
        plot_item_id = layout_plot.id()
        self.assertEqual(len(layout_plot.plot_settings), 1)
        # self.assertEqual(len(layout.items()), 0)
        layout.addLayoutItem(layout_plot)
        # self.assertEqual(len(layout.items()), 1)
        plot_dialog = PlotLayoutItemWidget(None, layout_plot)

        # add second plot
        plot_dialog.add_plot()
        self.assertEqual(len(layout_plot.plot_settings), 2)

        # edit first plot
        plot_dialog.setDockMode(True)
        plot_dialog.show_properties()
        plot_property_panel = plot_dialog.panel
        plot_property_panel.set_plot_type('violin')
        self.assertEqual(plot_property_panel.ptype, 'violin')
        plot_property_panel.acceptPanel()
        plot_property_panel.destroy()

        # edit second plot
        plot_dialog.plot_list.setCurrentRow(1)
        plot_dialog.show_properties()
        plot_property_panel = plot_dialog.panel
        plot_property_panel.set_plot_type('bar')
        self.assertEqual(plot_property_panel.ptype, 'bar')
        plot_property_panel.acceptPanel()
        plot_property_panel.destroy()

        # write xml

        xml_doc = QDomDocument('layout')
        element = layout.writeXml(xml_doc, QgsReadWriteContext())

        layout_plot.remove_plot(0)
        self.assertEqual(len(layout_plot.plot_settings), 1)
        self.assertEqual(layout_plot.plot_settings[0].plot_type, 'bar')

        layout_plot.remove_plot(0)
        self.assertEqual(len(layout_plot.plot_settings), 0)

        # read xml
        layout2 = QgsPrintLayout(project)
        self.assertTrue(
            layout2.readXml(element, xml_doc, QgsReadWriteContext()))
        layout_plot2 = layout2.itemById(plot_item_id)
        self.assertTrue(layout_plot2)

        self.assertEqual(len(layout_plot2.plot_settings), 2)
        self.assertEqual(layout_plot2.plot_settings[0].plot_type, 'violin')
        self.assertEqual(layout_plot2.plot_settings[1].plot_type, 'bar')
Example #25
0
    def testCombo(self):
        project = QgsProject()
        layout = QgsPrintLayout(project)

        combo = QgsLayoutItemComboBox(None)
        spy = QSignalSpy(combo.itemChanged)
        self.assertEqual(combo.count(), 0)
        self.assertIsNone(combo.currentLayout())
        self.assertIsNone(combo.currentItem())
        self.assertIsNone(combo.item(0))
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(1))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(combo.currentIndex(), -1)
        self.assertEqual(len(spy), 0)

        combo.setCurrentLayout(layout)
        self.assertEqual(combo.currentIndex(), -1)
        self.assertEqual(len(spy), 0)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertEqual(combo.count(), 0)
        self.assertIsNone(combo.currentItem())
        self.assertIsNone(combo.item(0))
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(1))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 0)

        combo.setAllowEmptyItem(True)
        self.assertEqual(combo.currentIndex(), 0)
        self.assertEqual(combo.count(), 1)
        self.assertEqual(len(spy), 2)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertIsNone(combo.currentItem())
        self.assertIsNone(combo.item(0))
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(1))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 2)
        self.assertEqual(combo.currentIndex(), 0)
        self.assertEqual(combo.itemText(0), '')

        combo.setAllowEmptyItem(False)
        self.assertEqual(combo.currentIndex(), -1)
        self.assertEqual(combo.count(), 0)
        self.assertEqual(len(spy), 4)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertIsNone(combo.currentItem())
        self.assertIsNone(combo.item(0))
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(1))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 4)
        self.assertEqual(combo.currentIndex(), -1)

        label1 = QgsLayoutItemLabel(layout)
        label1.setId('llll')
        # don't add to layout yet!
        combo.setItem(label1)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 4)
        self.assertEqual(combo.currentIndex(), -1)
        combo.setAllowEmptyItem(True)
        combo.setItem(label1)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 6)
        self.assertEqual(combo.currentIndex(), 0)
        combo.setAllowEmptyItem(False)

        layout.addLayoutItem(label1)
        self.assertEqual(combo.currentIndex(), 0)
        self.assertEqual(combo.count(), 1)
        self.assertEqual(combo.itemText(0), 'llll')
        self.assertEqual(len(spy), 10)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertEqual(combo.currentItem(), label1)
        self.assertEqual(combo.item(0), label1)
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(1))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 11)
        self.assertEqual(combo.currentIndex(), -1)
        combo.setItem(label1)
        self.assertEqual(combo.currentItem(), label1)
        self.assertEqual(len(spy), 12)
        self.assertEqual(combo.currentIndex(), 0)

        combo.setAllowEmptyItem(True)
        self.assertEqual(combo.currentIndex(), 1)
        self.assertEqual(combo.count(), 2)
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'llll')
        self.assertEqual(len(spy), 13)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertEqual(combo.currentItem(), label1)
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label1)
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(2))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 14)
        self.assertEqual(combo.currentIndex(), 0)
        combo.setItem(label1)
        self.assertEqual(combo.currentItem(), label1)
        self.assertEqual(len(spy), 15)
        self.assertEqual(combo.currentIndex(), 1)

        label2 = QgsLayoutItemLabel(layout)
        label2.setId('mmmm')
        layout.addLayoutItem(label2)
        self.assertEqual(combo.currentIndex(), 1)
        self.assertEqual(combo.count(), 3)
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'llll')
        self.assertEqual(combo.itemText(2), 'mmmm')
        self.assertEqual(len(spy), 15)
        self.assertEqual(combo.currentLayout(), layout)
        self.assertEqual(combo.currentItem(), label1)
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label1)
        self.assertEqual(combo.item(2), label2)
        self.assertIsNone(combo.item(-1))
        self.assertIsNone(combo.item(3))
        combo.setItem(None)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(len(spy), 16)
        self.assertEqual(combo.currentIndex(), 0)
        combo.setItem(label1)
        self.assertEqual(combo.currentItem(), label1)
        self.assertEqual(len(spy), 17)
        self.assertEqual(combo.currentIndex(), 1)

        label1.setId('nnnn')
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'mmmm')
        self.assertEqual(combo.itemText(2), 'nnnn')
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label2)
        self.assertEqual(combo.item(2), label1)
        self.assertEqual(combo.currentItem(), label1)

        combo.setAllowEmptyItem(False)
        self.assertEqual(combo.itemText(0), 'mmmm')
        self.assertEqual(combo.itemText(1), 'nnnn')
        self.assertEqual(combo.item(0), label2)
        self.assertEqual(combo.item(1), label1)
        self.assertEqual(combo.currentItem(), label1)

        combo.setItem(label2)
        label2.setId('oooo')
        self.assertEqual(combo.itemText(0), 'nnnn')
        self.assertEqual(combo.itemText(1), 'oooo')
        self.assertEqual(combo.item(0), label1)
        self.assertEqual(combo.item(1), label2)
        self.assertEqual(combo.currentItem(), label2)

        combo.setAllowEmptyItem(True)
        layout.removeLayoutItem(label1)
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'oooo')
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label2)
        self.assertEqual(combo.currentItem(), label2)

        map = QgsLayoutItemMap(layout)
        layout.addLayoutItem(map)
        map.setId('pppp')
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'oooo')
        self.assertEqual(combo.itemText(2), 'pppp')
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label2)
        self.assertEqual(combo.item(2), map)
        self.assertEqual(combo.currentItem(), label2)

        combo.setItemType(QgsLayoutItemRegistry.LayoutMap)
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'pppp')
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), map)
        self.assertIsNone(combo.currentItem())

        combo.setItemType(QgsLayoutItemRegistry.LayoutLabel)
        self.assertEqual(combo.itemText(0), '')
        self.assertEqual(combo.itemText(1), 'oooo')
        self.assertIsNone(combo.item(0))
        self.assertEqual(combo.item(1), label2)
        self.assertIsNone(combo.currentItem())

        combo.setItemType(QgsLayoutItemRegistry.LayoutAttributeTable)
        self.assertEqual(combo.itemText(0), '')
        self.assertIsNone(combo.item(0))
        self.assertIsNone(combo.currentItem())

        combo.setAllowEmptyItem(False)
        self.assertEqual(combo.count(), 0)
        self.assertIsNone(combo.currentItem())
        self.assertEqual(combo.currentIndex(), -1)
Example #26
0
def qgis_composer_html_renderer(impact_report, component):
    """HTML to PDF renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context for html input.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context

    # QGIS3: not used
    # qgis_composition_context = impact_report.qgis_composition_context

    # create new layout with A4 portrait page
    layout = QgsPrintLayout(QgsProject.instance())
    page = QgsLayoutItemPage(layout)
    page.setPageSize('A4', orientation=QgsLayoutItemPage.Portrait)
    layout.pageCollection().addPage(page)

    if not context.html_frame_elements:
        # if no html frame elements at all, do not generate empty report.
        component.output = ''
        return component.output

    # Add HTML Frame
    for html_el in context.html_frame_elements:
        mode = html_el.get('mode')
        html_element = QgsLayoutItemHtml(layout)
        margin_left = html_el.get('margin_left', 10)
        margin_top = html_el.get('margin_top', 10)
        width = html_el.get('width', component.page_width - 2 * margin_left)
        height = html_el.get('height', component.page_height - 2 * margin_top)

        html_frame = QgsLayoutFrame(layout, html_element)
        html_frame.attemptSetSceneRect(
            QRectF(margin_left, margin_top, width, height))
        html_element.addFrame(html_frame)

        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsLayoutItemHtml.ManualHtml)
                html_element.setResizeMode(
                    QgsLayoutItemHtml.RepeatUntilFinished)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsLayoutItemHtml.Url)
                html_element.setResizeMode(
                    QgsLayoutItemHtml.RepeatUntilFinished)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    # Attempt on removing blank page. Notes: We assume that the blank page
    # will always appears in the last x page(s), not in the middle.

    pc = layout.pageCollection()
    index = pc.pageCount()
    while pc.pageIsEmpty(index):
        pc.deletePage(index)
        index -= 1

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    output_format = component.output_format

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     layout, each_format,
                                                     component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(each_path, layout)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in list(output_format.items()):
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(impact_report, each_path,
                                                     layout, each_format,
                                                     component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(each_path, layout)
                component_output[key] = result_path
    elif (output_format
          in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(impact_report,
                                                 component_output_path, layout,
                                                 output_format, component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(component_output_path,
                                                      layout)
            component_output = result_path

    component.output = component_output

    return component.output