示例#1
0
    def test_dates(self):  # pylint: disable=too-many-statements
        """
        Test handling of dates
        """
        # default plot settings
        settings = PlotSettings('scatter')

        # no source layer, fixed values must be used
        settings.source_layer_id = ''
        settings.x = [QDate(2020, 1, 1), QDate(2020, 2, 1), QDate(2020, 3, 1)]
        settings.y = [4, 5, 6]
        factory = PlotFactory(settings)

        # Build the dictionary from teh figure
        plot_dict = factory.build_plot_dict()

        # get the x and y fields as list
        for items in plot_dict['data']:
            # converts the QDate into strings
            x = [str(i.toPyDate()) for i in items['x']]
            y = items['y']

        self.assertEqual(x, ["2020-01-01", "2020-02-01", "2020-03-01"])
        self.assertEqual(y, [4, 5, 6])

        settings.x = [
            QDateTime(2020, 1, 1, 11, 21),
            QDateTime(2020, 2, 1, 0, 15),
            QDateTime(2020, 3, 1, 17, 23, 11)
        ]
        settings.y = [4, 5, 6]
        factory = PlotFactory(settings)

        # Build the dictionary from teh figure
        plot_dict = factory.build_plot_dict()

        # get the x and y fields as list
        for items in plot_dict['data']:
            # converts the QDate into strings
            x = [str(i.toString(Qt.ISODate)) for i in items['x']]
            y = items['y']

        self.assertEqual(x, [
            "2020-01-01T11:21:00", "2020-02-01T00:15:00", "2020-03-01T17:23:11"
        ])
        self.assertEqual(y, [4, 5, 6])
    def test_data_defined_stroke_color(self):
        """
        Test data defined stroke color
        """
        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        settings = PlotSettings('scatter')
        settings.source_layer_id = vl1.id()
        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'mg'
        settings.properties['in_color'] = 'red'

        factory = PlotFactory(settings)
        # should be empty, not using data defined size
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.data_defined_colors, [])

        class TestGenerator(QgsExpressionContextGenerator):  # pylint: disable=missing-docstring, too-few-public-methods
            def createExpressionContext(self) -> QgsExpressionContext:  # pylint: disable=missing-docstring, no-self-use
                context = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                scope.setVariable('some_var', 10)
                context.appendScope(scope)
                context.appendScope(vl1.createExpressionContextScope())
                return context

        generator = TestGenerator()
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_STROKE_COLOR,
            QgsProperty.fromExpression(
                'case when round("ca"/@some_var)>10 then \'yellow\' else \'blue\' end'
            ))
        factory = PlotFactory(settings, context_generator=generator)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.data_defined_stroke_colors, [
            '#0000ff', '#0000ff', '#0000ff', '#0000ff', '#0000ff', '#ffff00',
            '#ffff00', '#ffff00', '#ffff00'
        ])
示例#3
0
    def test_visible_features(self):
        """
        Test filtering to visible features only
        """
        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        # not using visible features
        settings = PlotSettings('scatter')
        settings.source_layer_id = vl1.id()

        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'ca'

        rect = QgsReferencedRectangle(QgsRectangle(10.1, 43.5, 10.8, 43.85),
                                      QgsCoordinateReferenceSystem(4326))
        factory = PlotFactory(settings, visible_region=rect)
        spy = QSignalSpy(factory.plot_built)
        self.assertEqual(len(spy), 0)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(factory.settings.y, [
            81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45
        ])

        settings.properties['visible_features_only'] = True
        factory = PlotFactory(settings, visible_region=rect)
        spy = QSignalSpy(factory.plot_built)
        self.assertEqual(factory.settings.x, [88, 350, 151, 203])
        self.assertEqual(factory.settings.y, [22.26, 116.44, 108.25, 110.45])

        factory.set_visible_region(
            QgsReferencedRectangle(QgsRectangle(10.6, 43.1, 12, 43.8),
                                   QgsCoordinateReferenceSystem(4326)))
        self.assertEqual(len(spy), 1)
        self.assertEqual(factory.settings.x, [98, 267, 319, 137])
        self.assertEqual(factory.settings.y, [81.87, 74.16, 46.64, 126.73])

        # with reprojection
        factory.set_visible_region(
            QgsReferencedRectangle(
                QgsRectangle(1167379, 5310986, 1367180, 5391728),
                QgsCoordinateReferenceSystem(3857)))
        self.assertEqual(len(spy), 2)
        self.assertEqual(factory.settings.x, [98, 267, 329, 319, 137])
        self.assertEqual(factory.settings.y,
                         [81.87, 74.16, 35.05, 46.64, 126.73])
示例#4
0
    def test_set_default_settings(self):
        """
        Test setting dialog to a newly constructed settings object
        """
        settings = PlotSettings()
        dialog = DataPlotlyPanelWidget(None, iface=IFACE)
        dialog.set_settings(settings)

        self.assertEqual(dialog.get_settings().plot_type, settings.plot_type)
        for k in settings.properties.keys():
            if k in ['x', 'y', 'z', 'additional_hover_text', 'featureIds', 'featureBox', 'custom', 'in_color',
                     'marker_size']:
                continue

            print(k)
            self.assertEqual(dialog.get_settings().properties[k], settings.properties[k])
        for k in settings.layout.keys():
            print(k)
            self.assertEqual(dialog.get_settings().layout[k], settings.layout[k])
示例#5
0
    def test_data_defined_sizes(self):
        """
        Test data defined marker sizes
        """
        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        settings = PlotSettings('scatter')
        settings.source_layer_id = vl1.id()
        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'mg'
        settings.properties['marker_size'] = 15

        factory = PlotFactory(settings)
        # should be empty, not using data defined size
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.data_defined_marker_sizes, [])

        class TestGenerator(QgsExpressionContextGenerator):  # pylint: disable=missing-docstring, too-few-public-methods
            def createExpressionContext(self) -> QgsExpressionContext:  # pylint: disable=missing-docstring, no-self-use
                context = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                scope.setVariable('some_var', 10)
                context.appendScope(scope)
                context.appendScope(vl1.createExpressionContextScope())
                return context

        generator = TestGenerator()
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_MARKER_SIZE,
            QgsProperty.fromExpression('round("ca"/@some_var *@value)'))
        factory = PlotFactory(settings, context_generator=generator)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(
            factory.settings.data_defined_marker_sizes,
            [123.0, 33.0, 111.0, 53.0, 70.0, 190.0, 175.0, 162.0, 166.0])
示例#6
0
    def test_dates(self):  # pylint: disable=too-many-statements
        """
        Test handling of dates
        """
        # default plot settings
        settings = PlotSettings('scatter')

        # no source layer, fixed values must be used
        settings.source_layer_id = ''
        settings.x = [QDate(2020, 1, 1), QDate(2020, 2, 1), QDate(2020, 3, 1)]
        settings.y = [4, 5, 6]
        factory = PlotFactory(settings)

        # Build the HTML/JavaScript for the plot
        plot_html = factory.build_html({})

        # Find the plot specification in the HTML
        match = re.search(r'\[.*\]', plot_html)
        plot_dictionary = json.loads(match.group(0))[0]

        self.assertEqual(plot_dictionary['x'],
                         ["2020-01-01", "2020-02-01", "2020-03-01"])
        self.assertEqual(plot_dictionary['y'], [4, 5, 6])

        settings.x = [
            QDateTime(2020, 1, 1, 11, 21),
            QDateTime(2020, 2, 1, 0, 15),
            QDateTime(2020, 3, 1, 17, 23, 11)
        ]
        settings.y = [4, 5, 6]
        factory = PlotFactory(settings)

        # Build the HTML/JavaScript for the plot
        plot_html = factory.build_html({})

        # Find the plot specification in the HTML
        match = re.search(r'\[.*\]', plot_html)
        plot_dictionary = json.loads(match.group(0))[0]

        self.assertEqual(plot_dictionary['x'], [
            "2020-01-01T11:21:00", "2020-02-01T00:15:00", "2020-03-01T17:23:11"
        ])
        self.assertEqual(plot_dictionary['y'], [4, 5, 6])
    def test_read_write_project(self):
        """
        Test reading and writing to project document
        """
        # fake project document
        doc = QDomDocument("test")
        doc.appendChild(doc.createElement('qgis'))
        original = PlotSettings('test',
                                properties={
                                    'marker_width': 2,
                                    'marker_size': 5
                                },
                                layout={
                                    'title': 'my plot',
                                    'legend_orientation': 'v'
                                })
        original.write_to_project(doc)

        res = PlotSettings('gg')
        res.read_from_project(doc)
        self.assertEqual(res.plot_type, original.plot_type)
        self.assertEqual(res.properties, original.properties)
        self.assertEqual(res.layout, original.layout)
示例#8
0
    def __init__(self, layout):
        super().__init__(layout)
        self.setCacheMode(QGraphicsItem.NoCache)
        self.plot_settings = []
        self.plot_settings.append(PlotSettings())
        self.linked_map_uuid = ''
        self.linked_map = None

        self.web_page = LoggingWebPage(self)
        self.web_page.setNetworkAccessManager(QgsNetworkAccessManager.instance())

        # This makes the background transparent. (copied from QgsLayoutItemLabel)
        palette = self.web_page.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.web_page.setPalette(palette)
        self.web_page.mainFrame().setZoomFactor(10.0)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)

        self.web_page.loadFinished.connect(self.loading_html_finished)
        self.html_loaded = False
        self.html_units_to_layout_units = self.calculate_html_units_to_layout_units()

        self.sizePositionChanged.connect(self.refresh)
class PlotLayoutItem(QgsLayoutItem):
    def __init__(self, layout):
        super().__init__(layout)
        self.setCacheMode(QGraphicsItem.NoCache)
        self.plot_settings = PlotSettings()
        self.web_page = LoggingWebPage(self)
        self.web_page.setNetworkAccessManager(
            QgsNetworkAccessManager.instance())

        # This makes the background transparent. (copied from QgsLayoutItemLabel)
        palette = self.web_page.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.web_page.setPalette(palette)
        self.web_page.mainFrame().setZoomFactor(10.0)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal,
                                                     Qt.ScrollBarAlwaysOff)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical,
                                                     Qt.ScrollBarAlwaysOff)

        self.web_page.loadFinished.connect(self.loading_html_finished)
        self.html_loaded = False
        self.html_units_to_layout_units = self.calculate_html_units_to_layout_units(
        )

        self.sizePositionChanged.connect(self.refresh)

    def type(self):
        return ITEM_TYPE

    def icon(self):
        return GuiUtils.get_icon('dataplotly.svg')

    def calculate_html_units_to_layout_units(self):
        if not self.layout():
            return 1

        # Hm - why is this? Something internal in Plotly which is auto-scaling the html content?
        # we may need to expose this as a "scaling" setting

        return 72

    def set_plot_settings(self, settings):
        """
        Sets the plot settings to show in the item
        """
        self.plot_settings = settings
        self.html_loaded = False
        self.invalidateCache()

    def draw(self, context):
        if not self.html_loaded:
            self.load_content()

        # almost a direct copy from QgsLayoutItemLabel!
        painter = context.renderContext().painter()
        painter.save()

        # painter is scaled to dots, so scale back to layout units
        painter.scale(
            context.renderContext().scaleFactor() /
            self.html_units_to_layout_units,
            context.renderContext().scaleFactor() /
            self.html_units_to_layout_units)
        self.web_page.mainFrame().render(painter)
        painter.restore()

    def create_plot(self):
        factory = PlotFactory(self.plot_settings)
        config = {'displayModeBar': False, 'staticPlot': True}
        return factory.build_html(config)

    def load_content(self):
        self.html_loaded = False
        base_url = QUrl.fromLocalFile(
            self.layout().project().absoluteFilePath())
        self.web_page.setViewportSize(
            QSize(self.rect().width() * self.html_units_to_layout_units,
                  self.rect().height() * self.html_units_to_layout_units))
        self.web_page.mainFrame().setHtml(self.create_plot(), base_url)

    def writePropertiesToElement(self, element, document, _):
        element.appendChild(self.plot_settings.write_xml(document))
        return True

    def readPropertiesFromElement(self, element, document, context):
        res = self.plot_settings.read_xml(element.firstChildElement('Option'))
        self.html_loaded = False
        self.invalidateCache()
        return res

    def loading_html_finished(self):
        self.html_loaded = True
        self.invalidateCache()
        self.update()

    def refresh(self):
        super().refresh()
        self.html_loaded = False
        self.invalidateCache()
    def test_readwrite(self):
        """
        Test reading and writing plot settings from XML
        """
        doc = QDomDocument("properties")
        original = PlotSettings('test',
                                properties={
                                    'marker_width': 2,
                                    'marker_size': 5
                                },
                                layout={
                                    'title': 'my plot',
                                    'legend_orientation': 'v'
                                })
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_FILTER,
            QgsProperty.fromExpression('"ap">50'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_MARKER_SIZE,
            QgsProperty.fromExpression('5+6'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_COLOR, QgsProperty.fromExpression("'red'"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_STROKE_WIDTH,
            QgsProperty.fromExpression('12/2'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_TITLE,
            QgsProperty.fromExpression("concat('my', '_title')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_LEGEND_TITLE,
            QgsProperty.fromExpression("concat('my', '_legend')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_TITLE,
            QgsProperty.fromExpression("concat('my', '_x_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_TITLE,
            QgsProperty.fromExpression("concat('my', '_y_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Z_TITLE,
            QgsProperty.fromExpression("concat('my', '_z_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MAX, QgsProperty.fromExpression("+1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MAX, QgsProperty.fromExpression("+1*10"))
        elem = original.write_xml(doc)
        self.assertFalse(elem.isNull())

        res = PlotSettings('gg')
        # test reading a bad element
        bad_elem = QDomElement()
        self.assertFalse(res.read_xml(bad_elem))

        self.assertTrue(res.read_xml(elem))
        self.assertEqual(res.plot_type, original.plot_type)
        self.assertEqual(res.properties, original.properties)
        self.assertEqual(res.layout, original.layout)
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_FILTER),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_COLOR),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_COLOR))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MAX))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MAX))
    def test_read_write_file(self):
        """
        Test reading and writing configuration to files
        """
        original = PlotSettings('test',
                                properties={
                                    'marker_width': 2,
                                    'marker_size': 5
                                },
                                layout={
                                    'title': 'my plot',
                                    'legend_orientation': 'v'
                                })
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_FILTER,
            QgsProperty.fromExpression('"ap">50'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_MARKER_SIZE,
            QgsProperty.fromExpression('5+6'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_COLOR, QgsProperty.fromExpression("'red'"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_STROKE_WIDTH,
            QgsProperty.fromExpression('12/2'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_TITLE,
            QgsProperty.fromExpression("concat('my', '_title')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_LEGEND_TITLE,
            QgsProperty.fromExpression("concat('my', '_legend')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_TITLE,
            QgsProperty.fromExpression("concat('my', '_x_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_TITLE,
            QgsProperty.fromExpression("concat('my', '_y_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Z_TITLE,
            QgsProperty.fromExpression("concat('my', '_z_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MAX, QgsProperty.fromExpression("+1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MAX, QgsProperty.fromExpression("+1*10"))

        path = os.path.join(tempfile.gettempdir(), 'plot_config.xml')

        self.assertFalse(
            original.write_to_file('/nooooooooo/nooooooooooo.xml'))
        self.assertTrue(original.write_to_file(path))

        res = PlotSettings()
        self.assertFalse(res.read_from_file('/nooooooooo/nooooooooooo.xml'))
        self.assertTrue(res.read_from_file(path))

        self.assertEqual(res.plot_type, original.plot_type)
        self.assertEqual(res.properties, original.properties)
        self.assertEqual(res.layout, original.layout)
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_FILTER),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_COLOR),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_COLOR))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MAX))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MAX))
    def test_read_write_project2(self):
        """
        Test reading and writing to project, signals based
        """
        p = QgsProject()
        original = PlotSettings('test',
                                properties={
                                    'marker_width': 2,
                                    'marker_size': 5
                                },
                                layout={
                                    'title': 'my plot',
                                    'legend_orientation': 'v'
                                })
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_FILTER,
            QgsProperty.fromExpression('"ap">50'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_MARKER_SIZE,
            QgsProperty.fromExpression('5+6'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_COLOR, QgsProperty.fromExpression("'red'"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_STROKE_WIDTH,
            QgsProperty.fromExpression('12/2'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_TITLE,
            QgsProperty.fromExpression("concat('my', '_title')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_LEGEND_TITLE,
            QgsProperty.fromExpression("concat('my', '_legend')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_TITLE,
            QgsProperty.fromExpression("concat('my', '_x_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_TITLE,
            QgsProperty.fromExpression("concat('my', '_y_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Z_TITLE,
            QgsProperty.fromExpression("concat('my', '_z_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MAX, QgsProperty.fromExpression("+1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MAX, QgsProperty.fromExpression("+1*10"))

        self.test_read_write_project2_written = False

        def write(doc):
            self.test_read_write_project2_written = True
            original.write_to_project(doc)

        p.writeProject.connect(write)

        path = os.path.join(tempfile.gettempdir(),
                            'test_dataplotly_project.qgs')
        self.assertTrue(p.write(path))
        for _ in range(100):
            QCoreApplication.processEvents()
        self.assertTrue(self.test_read_write_project2_written)

        p2 = QgsProject()
        res = PlotSettings('gg')
        self.test_read_write_project2_read = False

        def read(doc):
            res.read_from_project(doc)
            self.test_read_write_project2_read = True

        p2.readProject.connect(read)
        self.assertTrue(p2.read(path))
        for _ in range(100):
            QCoreApplication.processEvents()
        self.assertTrue(self.test_read_write_project2_read)

        self.assertEqual(res.plot_type, original.plot_type)
        self.assertEqual(res.properties, original.properties)
        self.assertEqual(res.layout, original.layout)
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_FILTER),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_COLOR),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_COLOR))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MAX))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MAX))
    def test_read_write_project(self):
        """
        Test reading and writing to project document
        """
        # fake project document
        doc = QDomDocument("test")
        doc.appendChild(doc.createElement('qgis'))
        original = PlotSettings('test',
                                properties={
                                    'marker_width': 2,
                                    'marker_size': 5
                                },
                                layout={
                                    'title': 'my plot',
                                    'legend_orientation': 'v'
                                })
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_FILTER,
            QgsProperty.fromExpression('"ap">50'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_MARKER_SIZE,
            QgsProperty.fromExpression('5+6'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_COLOR, QgsProperty.fromExpression("'red'"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_STROKE_WIDTH,
            QgsProperty.fromExpression('12/2'))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_TITLE,
            QgsProperty.fromExpression("concat('my', '_title')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_LEGEND_TITLE,
            QgsProperty.fromExpression("concat('my', '_legend')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_TITLE,
            QgsProperty.fromExpression("concat('my', '_x_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_TITLE,
            QgsProperty.fromExpression("concat('my', '_y_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Z_TITLE,
            QgsProperty.fromExpression("concat('my', '_z_axis')"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MAX, QgsProperty.fromExpression("+1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MIN, QgsProperty.fromExpression("-1*10"))
        original.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MAX, QgsProperty.fromExpression("+1*10"))

        original.write_to_project(doc)

        res = PlotSettings('gg')
        res.read_from_project(doc)
        self.assertEqual(res.plot_type, original.plot_type)
        self.assertEqual(res.properties, original.properties)
        self.assertEqual(res.layout, original.layout)
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_FILTER),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_FILTER))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_MARKER_SIZE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_COLOR),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_COLOR))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_STROKE_WIDTH))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_LEGEND_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Z_TITLE))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_X_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_X_MAX))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MIN),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MIN))
        self.assertEqual(
            res.data_defined_properties.property(PlotSettings.PROPERTY_Y_MAX),
            original.data_defined_properties.property(
                PlotSettings.PROPERTY_Y_MAX))
示例#14
0
    def test_data_defined_layout_properties(self):  # pylint: disable=too-many-statements
        """
        Test data defined stroke color
        """
        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        settings = PlotSettings('scatter')
        settings.source_layer_id = vl1.id()
        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'mg'
        settings.layout['title'] = 'title'
        settings.layout['legend_title'] = 'legend_title'
        settings.layout['x_title'] = 'x_title'
        settings.layout['y_title'] = 'y_title'
        settings.layout['z_title'] = 'z_title'
        settings.layout['x_min'] = 0
        settings.layout['x_max'] = 1
        settings.layout['y_min'] = 0
        settings.layout['y_max'] = 1

        factory = PlotFactory(settings)
        # should be empty, not using data defined size
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.data_defined_title, '')
        self.assertEqual(factory.settings.data_defined_legend_title, '')
        self.assertEqual(factory.settings.data_defined_x_title, '')
        self.assertEqual(factory.settings.data_defined_y_title, '')
        self.assertEqual(factory.settings.data_defined_z_title, '')
        self.assertEqual(factory.settings.data_defined_x_min, None)
        self.assertEqual(factory.settings.data_defined_x_max, None)
        self.assertEqual(factory.settings.data_defined_y_min, None)
        self.assertEqual(factory.settings.data_defined_y_max, None)

        class TestGenerator(QgsExpressionContextGenerator):  # pylint: disable=missing-docstring, too-few-public-methods
            def createExpressionContext(self) -> QgsExpressionContext:  # pylint: disable=missing-docstring, no-self-use
                context = QgsExpressionContext()
                scope = QgsExpressionContextScope()
                scope.setVariable('some_var', 10)
                context.appendScope(scope)
                context.appendScope(vl1.createExpressionContextScope())
                return context

        generator = TestGenerator()
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_TITLE,
            QgsProperty.fromExpression("concat('my', '_title_', @some_var)"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_LEGEND_TITLE,
            QgsProperty.fromExpression("concat('my', '_legend_', @some_var)"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_TITLE,
            QgsProperty.fromExpression("concat('my', '_x_axis_', @some_var)"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_TITLE,
            QgsProperty.fromExpression("concat('my', '_y_axis_', @some_var)"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Z_TITLE,
            QgsProperty.fromExpression("concat('my', '_z_axis_', @some_var)"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MIN,
            QgsProperty.fromExpression("-1*@some_var"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_X_MAX,
            QgsProperty.fromExpression("+1*@some_var"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MIN,
            QgsProperty.fromExpression("-1*@some_var"))
        settings.data_defined_properties.setProperty(
            PlotSettings.PROPERTY_Y_MAX,
            QgsProperty.fromExpression("+1*@some_var"))
        factory = PlotFactory(settings, context_generator=generator)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(
            factory.settings.y,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.data_defined_title, 'my_title_10')
        self.assertEqual(factory.settings.data_defined_legend_title,
                         'my_legend_10')
        self.assertEqual(factory.settings.data_defined_x_title, 'my_x_axis_10')
        self.assertEqual(factory.settings.data_defined_y_title, 'my_y_axis_10')
        self.assertEqual(factory.settings.data_defined_z_title, 'my_z_axis_10')
        self.assertEqual(factory.settings.data_defined_x_min, -10)
        self.assertEqual(factory.settings.data_defined_x_max, 10)
        self.assertEqual(factory.settings.data_defined_y_min, -10)
        self.assertEqual(factory.settings.data_defined_y_max, 10)
示例#15
0
    def test_values(self):  # pylint: disable=too-many-statements
        """
        Test value collection
        """

        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        # default plot settings
        settings = PlotSettings('scatter')

        # no source layer, fixed values must be used
        settings.source_layer_id = ''
        settings.x = [1, 2, 3]
        settings.y = [4, 5, 6]
        settings.z = [7, 8, 9]

        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x, [1, 2, 3])
        self.assertEqual(factory.settings.y, [4, 5, 6])
        self.assertEqual(factory.settings.z, [7, 8, 9])
        self.assertEqual(factory.settings.additional_hover_text, [])

        # use source layer
        settings.source_layer_id = vl1.id()

        # no source set => no values
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x, [])
        self.assertEqual(factory.settings.y, [])
        self.assertEqual(factory.settings.z, [])
        self.assertEqual(factory.settings.additional_hover_text, [])

        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'ca'
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(factory.settings.y, [
            81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45
        ])
        self.assertEqual(factory.settings.z, [])
        self.assertEqual(factory.settings.additional_hover_text, [])

        # with z
        settings.properties['z_name'] = 'mg'
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x,
                         [98, 88, 267, 329, 319, 137, 350, 151, 203])
        self.assertEqual(factory.settings.y, [
            81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45
        ])
        self.assertEqual(
            factory.settings.z,
            [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34])
        self.assertEqual(factory.settings.additional_hover_text, [])

        # with expressions
        settings.properties['x_name'] = '"so4"/10'
        settings.properties[
            'y_name'] = 'case when "profm" >-16 then "ca" else "mg" end'
        settings.properties['z_name'] = 'case when $x < 10.5 then 1 else 0 end'
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x,
                         [9.8, 8.8, 26.7, 32.9, 31.9, 13.7, 35.0, 15.1, 20.3])
        self.assertEqual(
            factory.settings.y,
            [81.87, 86.03, 85.26, 35.05, 131.59, 95.36, 112.88, 108.25, 78.34])
        self.assertEqual(factory.settings.z, [0, 1, 0, 0, 0, 0, 0, 1, 1])
        self.assertEqual(factory.settings.additional_hover_text, [])

        # with some nulls
        settings.properties['x_name'] = '"so4"/10'
        settings.properties[
            'y_name'] = 'case when "profm" >-16 then "ca" else "mg" end'
        settings.properties[
            'z_name'] = 'case when $x < 10.5 then NULL else 1 end'
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x,
                         [9.8, 26.7, 32.9, 31.9, 13.7, 35.0])
        self.assertEqual(factory.settings.y,
                         [81.87, 85.26, 35.05, 131.59, 95.36, 112.88])
        self.assertEqual(factory.settings.z, [1, 1, 1, 1, 1, 1])
        self.assertEqual(factory.settings.additional_hover_text, [])

        # with additional values
        settings.layout['additional_info_expression'] = 'id'
        factory = PlotFactory(settings)
        self.assertEqual(factory.settings.x,
                         [9.8, 26.7, 32.9, 31.9, 13.7, 35.0])
        self.assertEqual(factory.settings.y,
                         [81.87, 85.26, 35.05, 131.59, 95.36, 112.88])
        self.assertEqual(factory.settings.z, [1, 1, 1, 1, 1, 1])
        self.assertEqual(factory.settings.additional_hover_text,
                         [9, 7, 6, 5, 4, 3])
示例#16
0
    def test_selected_feature_values_dynamic(self):
        """
        Test that factory proactively updates when a selection changes, when desired
        """

        layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp')

        vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        vl1.setSubsetString('id < 10')
        self.assertTrue(vl1.isValid())
        QgsProject.instance().addMapLayer(vl1)

        # not using selected features
        settings = PlotSettings('scatter')
        settings.properties['selected_features_only'] = False
        settings.source_layer_id = vl1.id()

        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'ca'
        factory = PlotFactory(settings)
        spy = QSignalSpy(factory.plot_built)
        vl1.selectByIds([1, 3, 4])
        self.assertEqual(len(spy), 0)

        # using selected features
        settings = PlotSettings('scatter')
        settings.properties['selected_features_only'] = True
        settings.source_layer_id = vl1.id()
        settings.properties['x_name'] = 'so4'
        settings.properties['y_name'] = 'ca'
        factory = PlotFactory(settings)
        spy = QSignalSpy(factory.plot_built)

        vl1.selectByIds([1])
        self.assertEqual(len(spy), 1)
        self.assertEqual(factory.settings.x, [88])
        self.assertEqual(factory.settings.y, [22.26])

        vl1.selectByIds([1, 3, 4])
        self.assertEqual(len(spy), 2)
        self.assertEqual(factory.settings.x, [88, 329, 319])
        self.assertEqual(factory.settings.y, [22.26, 35.05, 46.64])

        vl1.selectByIds([])
        self.assertEqual(len(spy), 3)
        self.assertEqual(factory.settings.x, [])
        self.assertEqual(factory.settings.y, [])
    def test_read_write_project(self):
        """
        Test saving/restoring dialog state in project
        """
        # print('read write project test')
        p = QgsProject.instance()
        dialog = DataPlotlyPanelWidget(None, override_iface=IFACE)
        dialog.set_plot_type('violin')

        # first, disable saving to project
        dialog.read_from_project = False
        dialog.save_to_project = False

        path = os.path.join(tempfile.gettempdir(),
                            'test_dataplotly_project.qgs')
        layer_path = os.path.join(os.path.dirname(__file__),
                                  'test_layer.geojson')

        # create QgsVectorLayer from path and test validity
        vl = QgsVectorLayer(layer_path, 'test_layer', 'ogr')
        self.assertTrue(vl.isValid())

        # print(dialog.layer_combo.currentLayer())

        self.assertTrue(p.write(path))

        res = PlotSettings()

        # def read(doc):
        #    self.assertTrue(res.read_from_project(doc))
        #    self.assertEqual(res.plot_type, 'violin')
        #    self.read_triggered = True

        p.clear()
        for _ in range(100):
            QCoreApplication.processEvents()

        self.assertTrue(p.read(path))
        self.assertEqual(res.plot_type, 'scatter')

        # TODO - enable when dialog can restore properties and avoid this fragile test
        # # enable saving to project
        # dialog.save_to_project = True
        # dialog.read_from_project = True
        # self.assertTrue(p.write(path))
        # for _ in range(100):
        #     QCoreApplication.processEvents()

        # p.clear()

        # p.readProject.connect(read)
        # self.assertTrue(p.read(path))
        # for _ in range(100):
        #     QCoreApplication.processEvents()

        # self.assertTrue(self.read_triggered)

        # todo - test that dialog can restore properties, but requires the missing set_settings method
        dialog.x_combo.setExpression('"Ca"')
        dialog.layer_combo.setLayer(vl)

        dialog.x_combo.currentText()

        self.assertTrue(dialog.x_combo.expression(), '"Ca"')
示例#18
0
class PlotLayoutItem(QgsLayoutItem):
    def __init__(self, layout):
        super().__init__(layout)
        self.setCacheMode(QGraphicsItem.NoCache)
        self.plot_settings = PlotSettings()
        self.linked_map_uuid = ''
        self.linked_map = None

        self.filter_by_map = False
        self.filter_by_atlas = False

        self.web_page = LoggingWebPage(self)
        self.web_page.setNetworkAccessManager(
            QgsNetworkAccessManager.instance())

        # This makes the background transparent. (copied from QgsLayoutItemLabel)
        palette = self.web_page.palette()
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.web_page.setPalette(palette)
        self.web_page.mainFrame().setZoomFactor(10.0)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Horizontal,
                                                     Qt.ScrollBarAlwaysOff)
        self.web_page.mainFrame().setScrollBarPolicy(Qt.Vertical,
                                                     Qt.ScrollBarAlwaysOff)

        self.web_page.loadFinished.connect(self.loading_html_finished)
        self.html_loaded = False
        self.html_units_to_layout_units = self.calculate_html_units_to_layout_units(
        )

        self.sizePositionChanged.connect(self.refresh)

    def type(self):
        return ITEM_TYPE

    def icon(self):
        return GuiUtils.get_icon('dataplotly.svg')

    def calculate_html_units_to_layout_units(self):
        if not self.layout():
            return 1

        # Hm - why is this? Something internal in Plotly which is auto-scaling the html content?
        # we may need to expose this as a "scaling" setting

        return 72

    def set_linked_map(self, map):
        """
        Sets the map linked to the plot item
        """
        if self.linked_map == map:
            return

        self.disconnect_current_map()

        self.linked_map = map
        self.linked_map.extentChanged.connect(self.map_extent_changed)
        self.linked_map.mapRotationChanged.connect(self.map_extent_changed)
        self.linked_map.destroyed.connect(self.disconnect_current_map)

    def disconnect_current_map(self):
        if not self.linked_map:
            return

        try:
            self.linked_map.extentChanged.disconnect(self.map_extent_changed)
            self.linked_map.mapRotationChanged.disconnect(
                self.map_extent_changed)
            self.linked_map.destroyed.disconnect(self.disconnect_current_map)
        except RuntimeError:
            # c++ object already gone!
            pass
        self.linked_map = None

    def set_plot_settings(self, settings):
        """
        Sets the plot settings to show in the item
        """
        self.plot_settings = settings
        self.html_loaded = False
        self.invalidateCache()

    def draw(self, context):
        if not self.html_loaded:
            self.load_content()

        # almost a direct copy from QgsLayoutItemLabel!
        painter = context.renderContext().painter()
        painter.save()

        # painter is scaled to dots, so scale back to layout units
        painter.scale(
            context.renderContext().scaleFactor() /
            self.html_units_to_layout_units,
            context.renderContext().scaleFactor() /
            self.html_units_to_layout_units)
        self.web_page.mainFrame().render(painter)
        painter.restore()

    def create_plot(self):
        if self.linked_map and self.filter_by_map:
            polygon_filter = FilterRegion(
                QgsGeometry.fromQPolygonF(
                    self.linked_map.visibleExtentPolygon()),
                self.linked_map.crs())
            self.plot_settings.properties['visible_features_only'] = True
        elif self.filter_by_atlas and self.layout().reportContext().layer(
        ) and self.layout().reportContext().feature().isValid():
            polygon_filter = FilterRegion(
                self.layout().reportContext().currentGeometry(),
                self.layout().reportContext().layer().crs())
            self.plot_settings.properties['visible_features_only'] = True
        else:
            polygon_filter = None
            self.plot_settings.properties['visible_features_only'] = False

        factory = PlotFactory(self.plot_settings,
                              self,
                              polygon_filter=polygon_filter)
        config = {'displayModeBar': False, 'staticPlot': True}
        return factory.build_html(config)

    def load_content(self):
        self.html_loaded = False
        base_url = QUrl.fromLocalFile(
            self.layout().project().absoluteFilePath())
        self.web_page.setViewportSize(
            QSize(self.rect().width() * self.html_units_to_layout_units,
                  self.rect().height() * self.html_units_to_layout_units))
        self.web_page.mainFrame().setHtml(self.create_plot(), base_url)

    def writePropertiesToElement(self, element, document, _):
        element.appendChild(self.plot_settings.write_xml(document))
        element.setAttribute('filter_by_map', 1 if self.filter_by_map else 0)
        element.setAttribute('filter_by_atlas',
                             1 if self.filter_by_atlas else 0)
        element.setAttribute('linked_map',
                             self.linked_map.uuid() if self.linked_map else '')
        return True

    def readPropertiesFromElement(self, element, document, context):
        res = self.plot_settings.read_xml(element.firstChildElement('Option'))

        self.filter_by_map = bool(int(element.attribute('filter_by_map', '0')))
        self.filter_by_atlas = bool(
            int(element.attribute('filter_by_atlas', '0')))
        self.linked_map_uuid = element.attribute('linked_map')
        self.disconnect_current_map()

        self.html_loaded = False
        self.invalidateCache()
        return res

    def finalizeRestoreFromXml(self):
        # has to happen after ALL items have been restored
        if self.layout() and self.linked_map_uuid:
            self.disconnect_current_map()
            map = self.layout().itemByUuid(self.linked_map_uuid)
            if map:
                self.set_linked_map(map)

    def loading_html_finished(self):
        self.html_loaded = True
        self.invalidateCache()
        self.update()

    def refresh(self):
        super().refresh()
        self.html_loaded = False
        self.invalidateCache()

    def map_extent_changed(self):
        if not self.linked_map or not self.filter_by_map:
            return

        self.html_loaded = False
        self.invalidateCache()

        self.update()
示例#19
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        :param parameters:
        :param context:
        """

        layer = self.parameterAsSource(parameters, self.INPUT, context)
        fields = layer.fields()

        xfieldname = self.parameterAsString(parameters, self.XFIELD, context)
        yfieldname = self.parameterAsString(parameters, self.YFIELD, context)

        outputHtmlFile = self.parameterAsFileOutput(parameters,
                                                    self.OUTPUT_HTML_FILE,
                                                    context)
        outputJsonFile = self.parameterAsFileOutput(parameters,
                                                    self.OUTPUT_JSON_FILE,
                                                    context)

        plot_type = 'bar'
        plot_type_input = self.parameterAsInt(parameters, self.PLOT_TYPE,
                                              context)
        plot_type = self.PLOT_TYPE_OPTIONS[plot_type_input]

        plot_title = self.parameterAsString(parameters, self.PLOT_TITLE,
                                            context)

        in_color_input = self.parameterAsInt(parameters, self.IN_COLOR,
                                             context)
        in_color_hex = self.IN_COLOR_OPTIONS[in_color_input]
        in_color_html = self.parameterAsString(parameters, self.IN_COLOR_HTML,
                                               context)

        # Some controls
        msg = []
        if plot_type in self.X_MANDATORY and not xfieldname:
            msg.append(self.tr("The chosen plot type needs a X field !"))
        if plot_type in self.Y_MANDATORY and not yfieldname:
            msg.append(self.tr("The chosen plot type needs a Y field !"))
        if msg:
            feedback.reportError(' '.join(msg))
            raise QgsProcessingException(msg)

        # Build needed dictionary
        settings = PlotSettings(plot_type)
        properties = {}

        # Add X dimension
        x_title = ''
        if xfieldname:
            # get field index for x
            idxX = layer.fields().lookupField(xfieldname)
            # get list of values for x
            x_var = [
                i[xfieldname]
                for i in layer.getFeatures(QgsFeatureRequest().setFlags(
                    QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([idxX
                                                                         ]))
            ]
            fieldTypeX = fields[idxX].type()
            x_title = fields[idxX].alias() or xfieldname
            properties.x = x_var

        # Add Y dimension
        y_title = ''
        if yfieldname:
            # get field index for y
            idxY = layer.fields().lookupField(yfieldname)
            # get list of values for y
            y_var = [
                i[yfieldname]
                for i in layer.getFeatures(QgsFeatureRequest().setFlags(
                    QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([idxY
                                                                         ]))
            ]
            y_title = fields[idxY].alias() or yfieldname
            properties.y = y_var

        # Draw only markers for scatter plot
        if plot_type in ['scatter', 'polar']:
            properties['marker'] = 'markers'

        # Colours
        properties['in_color'] = in_color_html or in_color_hex or 'DodgerBlue'

        # Add layout
        layout = {'title': plot_title or layer.sourceName()}
        if plot_type in self.X_MANDATORY:
            layout['x_title'] = x_title
        if plot_type in self.Y_MANDATORY:
            layout['y_title'] = y_title

        settings = PlotSettings(plot_type,
                                properties=properties,
                                layout=layout)

        # Create plot instance
        factory = PlotFactory(settings)

        # Prepare results
        results = {self.OUTPUT_HTML_FILE: None, self.OUTPUT_JSON_FILE: None}

        # Save plot as HTML
        if outputHtmlFile:
            standalone_plot_path = factory.build_figure()
            if os.path.isfile(standalone_plot_path):
                # html file output
                copyfile(standalone_plot_path, outputHtmlFile)
                results[self.OUTPUT_HTML_FILE] = outputHtmlFile

        # Save plot as JSON
        if outputJsonFile:
            ojson = {'data': trace, 'layout': plot_layout}
            with codecs.open(outputJsonFile, 'w', encoding='utf-8') as f:
                f.write(json.dumps(ojson))
                results[self.OUTPUT_JSON_FILE] = outputJsonFile

        return results