def testProjectStorage(self): # New project without fileName p0 = QgsProject() self.assertTrue(p0.auxiliaryStorage().isValid()) # Create new layers with key otherwise auxiliary layers are not # automacially created when added in project vl0 = createLayer() vl0Shp = writeShape(vl0, 'vl0.shp') vl1 = createLayer() vl1Shp = writeShape(vl1, 'vl1.shp') vl0 = QgsVectorLayer(vl0Shp, 'points', 'ogr') self.assertTrue(vl0.isValid()) vl1 = QgsVectorLayer(vl1Shp, 'points', 'ogr') self.assertTrue(vl1.isValid()) # Add layers to project and check underlying auxiliary layers p0.addMapLayers([vl0, vl1]) self.assertTrue(vl0.loadAuxiliaryLayer(p0.auxiliaryStorage(), 'pk')) self.assertTrue( vl1.loadAuxiliaryLayer(p0.auxiliaryStorage(), 'num_char')) al0 = vl0.auxiliaryLayer() al1 = vl1.auxiliaryLayer() self.assertEqual(al0.joinInfo().targetFieldName(), 'pk') self.assertEqual(al1.joinInfo().targetFieldName(), 'num_char') # Add a field in auxiliary layers pdef0 = QgsPropertyDefinition('propname', QgsPropertyDefinition.DataTypeNumeric, '', '', 'ut') self.assertTrue(al0.addAuxiliaryField(pdef0)) pdef1 = QgsPropertyDefinition('propname1', QgsPropertyDefinition.DataTypeString, '', '', 'ut') self.assertTrue(al1.addAuxiliaryField(pdef1)) # Check auxiliary fields names af0Name = QgsAuxiliaryLayer.nameFromProperty(pdef0, False) self.assertEqual(af0Name, 'ut_propname') af1Name = QgsAuxiliaryLayer.nameFromProperty(pdef1, False) self.assertEqual(af1Name, 'ut_propname1') # Set value for auxiliary fields req = QgsFeatureRequest().setFilterExpression("name = 'Honey'") f = QgsFeature() vl0.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) af0Name = QgsAuxiliaryLayer.nameFromProperty(pdef0, True) index0 = vl0.fields().indexOf(af0Name) vl0.changeAttributeValue(f.id(), index0, 333) req = QgsFeatureRequest().setFilterExpression("name = 'Apple'") f = QgsFeature() vl1.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) af1Name = QgsAuxiliaryLayer.nameFromProperty(pdef1, True) index1 = vl1.fields().indexOf(af1Name) vl1.changeAttributeValue(f.id(), index0, 'myvalue') req = QgsFeatureRequest().setFilterExpression("name = 'Orange'") f = QgsFeature() vl1.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) vl1.changeAttributeValue(f.id(), index0, 'myvalue1') # Save the project in a zip file f = tmpPath() + '.qgz' p0.write(f) # Open the zip file with embedded auxiliary storage p1 = QgsProject() p1.read(f) # Check that auxiliary fields are well loaded in layers self.assertEqual(len(p1.mapLayers().values()), 2) for vl in p1.mapLayers().values(): al = vl.auxiliaryLayer() self.assertEqual(len(al.auxiliaryFields()), 1) af = al.auxiliaryFields()[0] afPropDef = QgsAuxiliaryLayer.propertyDefinitionFromField(af) self.assertEqual(afPropDef.origin(), 'ut') if vl.auxiliaryLayer().joinInfo().targetFieldName() == 'pk': self.assertEqual(afPropDef.name(), 'propname') self.assertEqual(al.featureCount(), 1) req = QgsFeatureRequest().setFilterExpression("name = 'Honey'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index0], 333.0) else: # num_char self.assertEqual(al.featureCount(), 2) self.assertEqual(afPropDef.name(), 'propname1') req = QgsFeatureRequest().setFilterExpression("name = 'Apple'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index1], 'myvalue') req = QgsFeatureRequest().setFilterExpression( "name = 'Orange'") f = QgsFeature() vl.getFeatures(req).nextFeature(f) self.assertTrue(f.isValid()) self.assertEqual(f.attributes()[index1], 'myvalue1')
def testAuxiliaryFieldWidgets(self): # Init storage s = QgsAuxiliaryStorage() self.assertTrue(s.isValid()) # Create a new auxiliary layer with 'pk' as key vl = createLayer() pkf = vl.fields().field(vl.fields().indexOf('pk')) al = s.createAuxiliaryLayer(pkf, vl) self.assertTrue(al.isValid()) # Set the auxiliary layer to the vector layer vl.setAuxiliaryLayer(al) # Add a visible property p = QgsPropertyDefinition('propName', QgsPropertyDefinition.DataTypeNumeric, '', '', 'user') self.assertTrue(al.addAuxiliaryField(p)) index = al.indexOfPropertyDefinition(p) self.assertFalse(al.isHiddenProperty(index)) afName = QgsAuxiliaryLayer.nameFromProperty(p, True) index = vl.fields().indexOf(afName) setup = vl.editorWidgetSetup(index) self.assertEqual(setup.type(), '') tested = False for c in vl.attributeTableConfig().columns(): if c.name == afName: self.assertFalse(c.hidden) tested = True break self.assertTrue(tested) # Add a hidden property p = QgsPalLayerSettings.propertyDefinitions()[QgsPalLayerSettings.PositionX] self.assertTrue(al.addAuxiliaryField(p)) index = al.indexOfPropertyDefinition(p) self.assertTrue(al.isHiddenProperty(index)) afName = QgsAuxiliaryLayer.nameFromProperty(p, True) index = vl.fields().indexOf(afName) setup = vl.editorWidgetSetup(index) self.assertEqual(setup.type(), 'Hidden') tested = False for c in vl.attributeTableConfig().columns(): if c.name == afName: self.assertTrue(c.hidden) tested = True break self.assertTrue(tested) # Add a color property p = QgsSymbolLayer.propertyDefinitions()[QgsSymbolLayer.PropertyFillColor] self.assertTrue(al.addAuxiliaryField(p)) index = al.indexOfPropertyDefinition(p) self.assertFalse(al.isHiddenProperty(index)) afName = QgsAuxiliaryLayer.nameFromProperty(p, True) index = vl.fields().indexOf(afName) setup = vl.editorWidgetSetup(index) self.assertEqual(setup.type(), 'Color')
def testProjectColor(self): scheme = [ s for s in QgsApplication.colorSchemeRegistry().schemes() if isinstance(s, QgsProjectColorScheme) ][0] scheme.setColors([]) definition = QgsPropertyDefinition( 'test', 'test', QgsPropertyDefinition.ColorWithAlpha) button = QgsPropertyOverrideButton() button.init(0, QgsProperty(), definition) button.aboutToShowMenu() self.assertIn('Project Color', [a.text() for a in button.menu().actions()]) self.assertIn('Color', [a.text() for a in button.menu().actions()]) color_action = [ a for a in button.menu().actions() if a.text() == 'Color' ][0] self.assertEqual([a.text() for a in color_action.menu().actions()][0], 'No colors set') # add some project colors scheme.setColors([[QColor(255, 0, 0), 'color 1'], [QColor(255, 255, 0), 'burnt marigold']]) button.aboutToShowMenu() self.assertIn('Project Color', [a.text() for a in button.menu().actions()]) self.assertIn('Color', [a.text() for a in button.menu().actions()]) color_action = [ a for a in button.menu().actions() if a.text() == 'Color' ][0] self.assertEqual([a.text() for a in color_action.menu().actions()], ['color 1', 'burnt marigold']) button.menuActionTriggered(color_action.menu().actions()[1]) self.assertTrue(button.toProperty().isActive()) self.assertEqual(button.toProperty().asExpression(), 'project_color(\'burnt marigold\')') button.menuActionTriggered(color_action.menu().actions()[0]) self.assertTrue(button.toProperty().isActive()) self.assertEqual(button.toProperty().asExpression(), 'project_color(\'color 1\')') button.setToProperty( QgsProperty.fromExpression('project_color(\'burnt marigold\')')) button.aboutToShowMenu() color_action = [ a for a in button.menu().actions() if a.text() == 'Color' ][0] self.assertTrue(color_action.isChecked()) self.assertEqual( [a.isChecked() for a in color_action.menu().actions()], [False, True]) # should also see color menu for ColorNoAlpha properties definition = QgsPropertyDefinition('test', 'test', QgsPropertyDefinition.ColorNoAlpha) button = QgsPropertyOverrideButton() button.init(0, QgsProperty(), definition) button.aboutToShowMenu() self.assertIn('Project Color', [a.text() for a in button.menu().actions()]) self.assertIn('Color', [a.text() for a in button.menu().actions()]) # but no color menu for other types definition = QgsPropertyDefinition('test', 'test', QgsPropertyDefinition.Double) button = QgsPropertyOverrideButton() button.init(0, QgsProperty(), definition) button.aboutToShowMenu() self.assertNotIn('Project Color', [a.text() for a in button.menu().actions()]) self.assertNotIn('Color', [a.text() for a in button.menu().actions()])
class PlotSettings: # pylint: disable=too-many-instance-attributes """ The PlotSettings class encapsulates all settings relating to a plot, and contains methods for serializing and deserializing these settings. """ PROPERTY_FILTER = 1 PROPERTY_MARKER_SIZE = 2 PROPERTY_COLOR = 3 PROPERTY_STROKE_COLOR = 4 PROPERTY_STROKE_WIDTH = 5 DYNAMIC_PROPERTIES = { PROPERTY_FILTER: QgsPropertyDefinition('filter', 'Feature filter', QgsPropertyDefinition.Boolean), PROPERTY_MARKER_SIZE: QgsPropertyDefinition('marker_size', 'Marker size', QgsPropertyDefinition.DoublePositive), PROPERTY_COLOR: QgsPropertyDefinition('color', 'Color', QgsPropertyDefinition.ColorWithAlpha), PROPERTY_STROKE_COLOR: QgsPropertyDefinition('stroke_color', 'Stroke color', QgsPropertyDefinition.ColorWithAlpha), PROPERTY_STROKE_WIDTH: QgsPropertyDefinition('stroke_width', 'Stroke width', QgsPropertyDefinition.DoublePositive) } def __init__(self, plot_type: str = 'scatter', properties: dict = None, layout: dict = None, source_layer_id=None): # Define default plot dictionary used as a basis for plot initialization # prepare the default dictionary with None values # plot properties plot_base_properties = { 'marker': 'markers', 'custom': None, 'hover_text': None, 'additional_hover_text': None, 'x_name': '', 'y_name': '', 'z_name': '', 'in_color': '#8ebad9', 'out_color': '#1f77b4', 'marker_width': 1, 'marker_size': 10, 'marker_symbol': 0, 'line_dash': 'solid', 'box_orientation': 'v', 'box_stat': None, 'box_outliers': False, 'name': '', 'normalization': None, 'cont_type': 'fill', 'color_scale': None, 'show_lines': False, 'cumulative': False, 'show_colorscale_legend': False, 'invert_color_scale': False, 'invert_hist': 'increasing', 'bins': 0, 'selected_features_only': False, 'visible_features_only': False, 'color_scale_data_defined_in_check': False, 'color_scale_data_defined_in_invert_check': False, 'marker_type_combo': 'Points', 'point_combo': '', 'line_combo': 'Solid Line', 'contour_type_combo': 'Fill', 'show_lines_check': False, 'opacity': 1, 'violin_side': None, 'show_mean_line': False } # layout nested dictionary plot_base_layout = { 'title': 'Plot Title', 'legend': True, 'legend_title': None, 'legend_orientation': 'h', 'x_title': '', 'y_title': '', 'z_title': '', 'xaxis': None, 'bar_mode': None, 'x_type': None, 'y_type': None, 'x_inv': None, 'y_inv': None, 'range_slider': { 'borderwidth': 1, 'visible': False }, 'bargaps': 0, 'polar': { 'angularaxis': { 'direction': 'clockwise' } }, 'additional_info_expression': '', 'bins_check': False } self.plot_base_dic = { 'plot_type': None, 'layer': None, 'plot_prop': plot_base_properties, 'layout_prop': plot_base_layout } self.data_defined_properties = QgsPropertyCollection() # Set class properties - we use the base dictionaries, replacing base values with # those from the passed properties dicts if properties is None: self.properties = plot_base_properties else: self.properties = {**plot_base_properties, **properties} if layout is None: self.layout = plot_base_layout else: self.layout = {**plot_base_layout, **layout} self.plot_type = plot_type self.x = [] self.y = [] self.z = [] self.feature_ids = [] self.additional_hover_text = [] self.data_defined_marker_sizes = [] self.data_defined_colors = [] self.data_defined_stroke_colors = [] self.data_defined_stroke_widths = [] self.source_layer_id = source_layer_id def write_xml(self, document: QDomDocument): """ Writes the plot settings to an XML element """ element = QgsXmlUtils.writeVariant( { 'plot_type': self.plot_type, 'plot_properties': self.properties, 'plot_layout': self.layout, 'source_layer_id': self.source_layer_id, 'dynamic_properties': self.data_defined_properties.toVariant( PlotSettings.DYNAMIC_PROPERTIES) }, document) return element def read_xml(self, element: QDomElement) -> bool: """ Reads the plot settings from an XML element """ res = QgsXmlUtils.readVariant(element) if not isinstance(res, dict) or \ 'plot_type' not in res or \ 'plot_properties' not in res or \ 'plot_layout' not in res: return False self.plot_type = res['plot_type'] self.properties = res['plot_properties'] self.layout = res['plot_layout'] self.source_layer_id = res.get('source_layer_id', None) self.data_defined_properties.loadVariant( res.get('dynamic_properties', None), PlotSettings.DYNAMIC_PROPERTIES) return True def write_to_project(self, document: QDomDocument): """ Writes the settings to a project (represented by the given DOM document) """ elem = self.write_xml(document) parent_elem = document.createElement('DataPlotly') parent_elem.appendChild(elem) root_node = document.elementsByTagName("qgis").item(0) root_node.appendChild(parent_elem) def read_from_project(self, document: QDomDocument): """ Reads the settings from a project (represented by the given DOM document) """ root_node = document.elementsByTagName("qgis").item(0) if root_node.isNull(): return False node = root_node.toElement().firstChildElement('DataPlotly') if node.isNull(): return False elem = node.toElement() return self.read_xml(elem.firstChildElement()) def write_to_file(self, file_name: str) -> bool: """ Writes the settings to an XML file """ document = QDomDocument("dataplotly") elem = self.write_xml(document) document.appendChild(elem) try: with open(file_name, "w") as f: f.write('<?xml version="1.0" encoding="UTF-8"?>\n') f.write(document.toString()) return True except FileNotFoundError: return False def read_from_file(self, file_name: str) -> bool: """ Reads the settings from an XML file """ f = QFile(file_name) if f.open(QIODevice.ReadOnly): document = QDomDocument() if document.setContent(f): if self.read_xml(document.firstChildElement()): return True return False
def testCreateField(self): s = QgsAuxiliaryStorage() self.assertTrue(s.isValid()) # Create a new auxiliary layer with 'pk' as key vl = createLayer() pkf = vl.fields().field(vl.fields().indexOf('pk')) al = s.createAuxiliaryLayer(pkf, vl) self.assertTrue(al.isValid()) vl.setAuxiliaryLayer(al) prop = QgsPropertyDefinition() prop.setComment('test_field') prop.setDataType(QgsPropertyDefinition.DataTypeNumeric) prop.setOrigin('user') prop.setName('custom') self.assertTrue(al.addAuxiliaryField(prop)) prop = QgsPropertyDefinition() prop.setComment('test_field_string') prop.setDataType(QgsPropertyDefinition.DataTypeString) prop.setOrigin('user') prop.setName('custom') self.assertTrue(al.addAuxiliaryField(prop)) self.assertEqual(len(al.auxiliaryFields()), 2) self.assertEqual(al.auxiliaryFields()[0].name(), 'user_custom_test_field') self.assertEqual(al.auxiliaryFields()[0].type(), QVariant.Double) self.assertEqual(al.auxiliaryFields()[0].typeName(), 'Real') self.assertEqual(al.auxiliaryFields()[1].name(), 'user_custom_test_field_string') self.assertEqual(al.auxiliaryFields()[1].type(), QVariant.String) self.assertEqual(al.auxiliaryFields()[1].typeName(), 'String')
def initAlgorithm(self, config=None): # layer self.addParameter( QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) # x fields (or expression) self.addParameter( QgsProcessingParameterExpression( self.XEXPRESSION, self.tr('X Field'), parentLayerParameterName=self.INPUT)) # y field (or expression) self.addParameter( QgsProcessingParameterExpression( self.YEXPRESSION, self.tr('Y Field'), parentLayerParameterName=self.INPUT)) # size size_param = QgsProcessingParameterNumber(self.SIZE, self.tr('Marker size'), defaultValue=10) size_param.setIsDynamic(True) size_param.setDynamicLayerParameterName(self.INPUT) size_param.setDynamicPropertyDefinition( QgsPropertyDefinition( "SIZE", self.tr("Size"), QgsPropertyDefinition.Double, )) self.addParameter(size_param) # color color_param = QgsProcessingParameterColor(self.COLOR, self.tr('Color'), optional=True, defaultValue='#8ebad9') color_param.setIsDynamic(True) color_param.setDynamicLayerParameterName(self.INPUT) color_param.setDynamicPropertyDefinition( QgsPropertyDefinition( "COLOR", self.tr("Color"), QgsPropertyDefinition.Double, )) self.addParameter(color_param) facet_row = QgsProcessingParameterExpression( self.FACET_ROW, self.tr('Facet row'), parentLayerParameterName=self.INPUT) facet_row.setFlags(QgsProcessingParameterDefinition.FlagAdvanced | QgsProcessingParameterDefinition.FlagOptional) self.addParameter(facet_row) facet_col = QgsProcessingParameterExpression( self.FACET_COL, self.tr('Facet col'), optional=True, parentLayerParameterName=self.INPUT) facet_col.setFlags(QgsProcessingParameterDefinition.FlagAdvanced | QgsProcessingParameterDefinition.FlagOptional) self.addParameter(facet_col) # offline parameter offline_param = QgsProcessingParameterBoolean( self.OFFLINE, self.tr('Complete offline usage'), defaultValue=False) offline_param.setFlags(QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(offline_param) # html file output self.addParameter( QgsProcessingParameterFileDestination( self.OUTPUT_HTML_FILE, self.tr('Scatter Plot'), self.tr('HTML files (*.html)'))) # json file output self.addParameter( QgsProcessingParameterFileDestination( self.OUTPUT_JSON_FILE, self.tr('JSON file'), self.tr('JSON Files (*.json)'), createByDefault=False, optional=True))