コード例 #1
0
ファイル: NumberInputPanel.py プロジェクト: ufolr/QGIS
class ModelerNumberInputPanel(BASE, WIDGET):
    """
    Number input panel for use inside the modeler - this input panel
    is based off the base input panel and includes a text based line input
    for entering values. This allows expressions and other non-numeric
    values to be set, which are later evalauted to numbers when the model
    is run.
    """

    hasChanged = pyqtSignal()

    def __init__(self, param, modelParametersDialog):
        super().__init__(None)
        self.setupUi(self)

        self.param = param
        self.modelParametersDialog = modelParametersDialog
        if param.defaultValue():
            self.setValue(param.defaultValue())
        self.btnSelect.clicked.connect(self.showExpressionsBuilder)
        self.leText.textChanged.connect(lambda: self.hasChanged.emit())

    def showExpressionsBuilder(self):
        context = createExpressionContext()
        processing_context = createContext()
        scope = self.modelParametersDialog.model.createExpressionContextScopeForChildAlgorithm(
            self.modelParametersDialog.childId, processing_context)
        context.appendScope(scope)

        highlighted = scope.variableNames()
        context.setHighlightedVariables(highlighted)

        dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self,
                                         'generic', context)

        dlg.setWindowTitle(self.tr('Expression Based Input'))
        if dlg.exec_() == QDialog.Accepted:
            exp = QgsExpression(dlg.expressionText())
            if not exp.hasParserError():
                self.setValue(dlg.expressionText())

    def getValue(self):
        value = self.leText.text()
        for param in self.modelParametersDialog.model.parameterDefinitions():
            if isinstance(param, QgsProcessingParameterNumber):
                if "@" + param.name() == value.strip():
                    return QgsProcessingModelChildParameterSource.fromModelParameter(
                        param.name())

        for alg in list(
                self.modelParametersDialog.model.childAlgorithms().values()):
            for out in alg.algorithm().outputDefinitions():
                if isinstance(out, QgsProcessingOutputNumber) and "@%s_%s" % (
                        alg.childId(), out.name()) == value.strip():
                    return QgsProcessingModelChildParameterSource.fromChildOutput(
                        alg.childId(), out.outputName())

        try:
            return float(value.strip())
        except:
            return QgsProcessingModelChildParameterSource.fromExpression(
                self.leText.text())

    def setValue(self, value):
        if isinstance(value, QgsProcessingModelChildParameterSource):
            if value.source(
            ) == QgsProcessingModelChildParameterSource.ModelParameter:
                self.leText.setText('@' + value.parameterName())
            elif value.source(
            ) == QgsProcessingModelChildParameterSource.ChildOutput:
                name = "%s_%s" % (value.outputChildId(), value.outputName())
                self.leText.setText(name)
            elif value.source(
            ) == QgsProcessingModelChildParameterSource.Expression:
                self.leText.setText(value.expression())
            else:
                self.leText.setText(str(value.staticValue()))
        else:
            self.leText.setText(str(value))
コード例 #2
0
ファイル: mock_iface.py プロジェクト: minff/xyz-qgis-plugin
class MyCanvas(QgsMapCanvas):
    closed = pyqtSignal()
    def closeEvent(self,event):
        super().closeEvent(event)
        print("closing")
        self.closed.emit()
コード例 #3
0
class Project(QObject):
    layer_added = pyqtSignal(str)

    def __init__(self, auto_transaction=True, evaluate_default_values=True):
        QObject.__init__(self)
        self.crs = None
        self.name = 'Not set'
        self.layers = List[Layer]
        self.legend = LegendGroup()
        self.auto_transaction = auto_transaction
        self.evaluate_default_values = evaluate_default_values
        self.relations = List[Relation]

        # {Layer_class_name: {dbattribute: {Layer_class, cardinality, Layer_domain, key_field, value_field]}
        self.bags_of_enum = dict()

    def add_layer(self, layer):
        self.layers.append(layer)

    def dump(self):
        definition = dict()
        definition['crs'] = self.crs.toWkt()
        definition['auto_transaction'] = self.auto_transaction
        definition['evaluate_default_values'] = self.evaluate_default_values

        legend = list()
        for layer in self.layers:
            legend.append(layer.dump())

        relations = list()

        for relation in self.relations:
            relations.append(relation.dump())

        definition['legend'] = legend
        definition['relations'] = relations

        return definition

    def load(self, definition):
        self.crs = definition['crs']
        self.auto_transaction = definition['auto_transaction']
        self.evaluate_default_values = definition['evaluate_default_values']

        self.layers = list()
        for layer_definition in definition['layers']:
            layer = Layer()
            layer.load(layer_definition)
            self.layers.append(layer)

    def create(self, path: str, qgis_project: QgsProject):
        qgis_project.setAutoTransaction(self.auto_transaction)
        qgis_project.setEvaluateDefaultValues(self.evaluate_default_values)
        qgis_layers = list()
        for layer in self.layers:
            qgis_layer = layer.create()
            self.layer_added.emit(qgis_layer.id())
            if not self.crs and qgis_layer.isSpatial():
                self.crs = qgis_layer.crs()

            qgis_layers.append(qgis_layer)

        qgis_project.addMapLayers(qgis_layers, not self.legend)

        if self.crs:
            if isinstance(self.crs, QgsCoordinateReferenceSystem):
                qgis_project.setCrs(self.crs)
            else:
                qgis_project.setCrs(
                    QgsCoordinateReferenceSystem.fromEpsgId(self.crs))

        qgis_relations = list(
            qgis_project.relationManager().relations().values())
        dict_domains = {
            layer.layer.id(): layer.is_domain
            for layer in self.layers
        }
        for relation in self.relations:
            rel = relation.create(qgis_project, qgis_relations)
            assert rel.isValid()
            qgis_relations.append(rel)

            if rel.referencedLayerId() in dict_domains and dict_domains[
                    rel.referencedLayerId()]:
                editor_widget_setup = QgsEditorWidgetSetup(
                    'RelationReference', {
                        'Relation': rel.id(),
                        'ShowForm': False,
                        'OrderByValue': True,
                        'ShowOpenFormButton': False
                    })
            else:
                editor_widget_setup = QgsEditorWidgetSetup(
                    'RelationReference', {
                        'Relation': rel.id(),
                        'ShowForm': False,
                        'OrderByValue': True,
                        'ShowOpenFormButton': False,
                        'AllowAddFeatures': True
                    })

            referencing_layer = rel.referencingLayer()
            referencing_layer.setEditorWidgetSetup(rel.referencingFields()[0],
                                                   editor_widget_setup)

        qgis_project.relationManager().setRelations(qgis_relations)

        # Set Bag of Enum widget
        for layer_name, bag_of_enum in self.bags_of_enum.items():
            for attribute, bag_of_enum_info in bag_of_enum.items():
                layer_obj = bag_of_enum_info[0]
                cardinality = bag_of_enum_info[1]
                domain_table = bag_of_enum_info[2]
                key_field = bag_of_enum_info[3]
                value_field = bag_of_enum_info[4]

                allow_null = cardinality.startswith('0')
                allow_multi = cardinality.endswith('*')

                current_layer = layer_obj.create()

                field_widget = 'ValueRelation'
                field_widget_config = {
                    'AllowMulti': allow_multi,
                    'UseCompleter': False,
                    'Value': value_field,
                    'OrderByValue': False,
                    'AllowNull': allow_null,
                    'Layer': domain_table.create().id(),
                    'FilterExpression': '',
                    'Key': key_field,
                    'NofColumns': 1
                }

                field_idx = current_layer.fields().indexOf(attribute)
                setup = QgsEditorWidgetSetup(field_widget, field_widget_config)
                current_layer.setEditorWidgetSetup(field_idx, setup)

        for layer in self.layers:
            layer.create_form(self)

        if self.legend:
            self.legend.create(qgis_project)

        if path:
            qgis_project.write(path)

    def post_generate(self):
        for layer in self.layers:
            layer.post_generate(self)
コード例 #4
0
class BuildingsDockwidget(QDockWidget, FORM_CLASS):
    closed = pyqtSignal()
    in_focus = pyqtSignal()

    frames = {}
    current_frame = None

    def __init__(self, parent=None):
        """Constructor."""
        super(BuildingsDockwidget, self).__init__(parent)

        # Set up the user interface from Designer.
        self.setupUi(self)

        # Set focus policy so can track when user clicks back onto dock widget
        self.setFocusPolicy(Qt.StrongFocus)

        self.prev_width = self.width()

        # Change look of options list widget
        self.lst_options.setStyleSheet(""" QListWidget {
                    background-color: rgb(69, 69, 69, 0);
                    outline: 0;
                }
                QListWidget::item {
                    color: white;
                    padding-top: 3px;
                    padding-bottom: 3px;
                }
                QListWidget::item::selected {
                    color: black;
                    background-color:palette(Window);
                    padding-right: 0px;
                };
            """)

        # Change look of sub menu list widget
        self.lst_sub_menu.setStyleSheet(""" QListWidget {
                    background-color: rgb(69, 69, 69, 0);
                    outline: 0;
                }
                QListWidget::item {
                    color: white;
                    padding-top: 3px;
                    padding-bottom: 3px;
                }
                QListWidget::item::selected {
                    color: black;
                    background-color:palette(Window);
                    padding-right: 0px;
                };
            """)

        self.frm_options.setStyleSheet(""" QFrame {
                    background-color: rgb(69, 69, 69, 220);
                };
            """)

        # Signals for clicking on list widgets
        self.lst_options.itemClicked.connect(self.show_selected_option)
        self.lst_sub_menu.itemSelectionChanged.connect(self.show_frame)

        self.splitter.splitterMoved.connect(self.resize_dockwidget)

        from buildings.utilities import database as db
        from buildings.gui.new_capture_source import NewCaptureSource
        from buildings.gui.bulk_load_frame import BulkLoadFrame
        from buildings.gui.alter_building_relationships import AlterRelationships
        from buildings.gui.production_frame import ProductionFrame
        from buildings.gui.new_entry import NewEntry
        from buildings.gui.new_capture_source_area import NewCaptureSourceArea
        from buildings.gui.reference_data import UpdateReferenceData

        self.db = db
        self.new_capture_source = NewCaptureSource
        self.bulk_load_frame = BulkLoadFrame
        self.alter_relationships = AlterRelationships
        self.production_frame = ProductionFrame
        self.new_entry = NewEntry
        self.new_capture_source_area = NewCaptureSourceArea
        self.reference_data = UpdateReferenceData

    @pyqtSlot()
    def show_selected_option(self):
        if self.lst_options.selectedItems():
            current = self.lst_options.selectedItems()[0]
            if current.text() == "Buildings":
                if isinstance(self.current_frame, self.alter_relationships):
                    self.current_frame.close_frame()
                if isinstance(self.current_frame,
                              self.new_capture_source_area):
                    self.current_frame.close_frame()
                try:
                    self.current_frame.close_frame()
                except AttributeError:
                    pass
                project.SRID = 2193
                project.set_crs()
                self.stk_options.removeWidget(self.stk_options.currentWidget())
                self.stk_options.addWidget(self.frames["menu_frame"])
                self.current_frame = self.frames["menu_frame"]
            self.lst_sub_menu.clearSelection()

    @pyqtSlot()
    def show_frame(self):
        if self.lst_sub_menu.selectedItems():
            current = self.lst_sub_menu.selectedItems()[0]
            # Remove the current widget and run its exit method
            # If it has no exit method, just remove the current widget
            if isinstance(self.current_frame, self.alter_relationships):
                self.current_frame.close_frame()
            if isinstance(self.current_frame, self.new_capture_source_area):
                self.current_frame.close_frame()
            try:
                self.current_frame.close_frame()
            except AttributeError:
                pass

            self.stk_options.removeWidget(self.stk_options.currentWidget())

            if current.text() == "Capture Sources":
                self.new_widget(self.new_capture_source(self))
            elif current.text() == "Bulk Load":
                self.new_widget(self.bulk_load_frame(self))
            elif current.text() == "Edit Outlines":
                self.new_widget(self.production_frame(self))
            elif current.text() == "Settings":
                self.new_widget(self.new_entry(self))
            elif current.text() == "Reference Data":
                self.new_widget(self.reference_data(self))

    def new_widget(self, frame):
        self.stk_options.addWidget(frame)
        self.stk_options.setCurrentIndex(1)
        self.current_frame = frame

    # insert into dictionary
    def insert_into_frames(self, text, object):
        self.frames[text] = object

    @pyqtSlot(int, int)
    def resize_dockwidget(self, pos, index):
        self.prev_width = self.width()
        if pos < 175:
            new_pos = 175 - pos
            new_dock_width = 600 - new_pos
            if new_dock_width > self.prev_width:
                if (new_dock_width + 5) > 600:
                    self.setFixedWidth(600)
                else:
                    self.setFixedWidth(new_dock_width + 5)
            else:
                self.setFixedWidth(new_dock_width)

    def closeEvent(self, event):
        self.closed.emit()
        event.accept()

    def defaultStyle(self):
        """default tab Widget style"""

        return """
            QTabWidget::pane {
                /* The tab widget frame */
                border-top: 2px solid #C2C7CB;
            }
            QTabWidget::tab-bar {
                /* move to the right by 5px */
                left: 5px;
            }

            QTabBar::tab {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E1E1E1,
                    stop: 0.4 #e7e7e7, stop: 0.5 #e7e7e7, stop: 1.0 #D3D3D3
                );
                border: 2px solid #C4C4C3;
                border-bottom-color: #C2C7CB;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
                padding: 3px;
            }
            QTabBar::tab:selected, QTabBar::tab:hover {
                background: qlineargradient(
                    x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ececec,
                    stop: 0.4 #f4f4f4, stop: 0.5 #e7e7e7, stop: 1.0 #ececec
                );
            }
            QTabBar::tab:selected {
                border-color: #9B9B9B;
                border-bottom-color: #f0f0f0; /* same as pane color */
            }
            QTabBar::tab:!selected {
                /* make non-selected tabs look smaller */
                margin-top: 2px;
            }
            QTabBar::tab:selected {
                /* expand to the left and right by 4px */
                margin-left: 0px;
                margin-right: 0px;
            }
            QTabBar::tab:first:selected {
                /* the first selected tab has nothing to overlap with on the left */
                margin-left: 0;
            }
            QTabBar::tab:last:selected {
                /* the last selected tab has nothing to overlap with on the right */
                margin-right: 0;
            }
            QTabBar::tab:only-one {
                /* if there is only one tab, we don't want overlapping margins */
                margin: 1;
            }
        """

    def focusInEvent(self, event):
        self.in_focus.emit()
コード例 #5
0
class DatabaseParameterWidget(QtWidgets.QWidget, FORM_CLASS):
    filesSelected = pyqtSignal()
    changeSize = pyqtSignal()
    def __init__(self, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)
        self.serverAbstractDb = None
        self.selectedAbstractDb = None
        self.setInitialState()
        self.useFrame = True
    
    @pyqtSlot(AbstractDb, name = 'on_comboBoxPostgis_dbChanged') #check this out luiz! hahahahaha
    def populateSelectedAbstractDb(self, abstractDb):
        self.selectedAbstractDb = abstractDb
        if self.useFrame:
            self.populateFrameComboBox()
    
    def populateFrameComboBox(self):
        if self.selectedAbstractDb:
            areaDict = self.selectedAbstractDb.getGeomColumnDictV2(primitiveFilter=['a'], excludeValidation = True)
            self.frameComboBox.clear()
            self.frameComboBox.addItem(self.tr('Select a table from database'))
            self.tableDict = dict()
            sortedKeys = list(areaDict.keys())
            sortedKeys.sort()
            for key in sortedKeys:
                tableKey = '{0}.{1}:{2}'.format(areaDict[key]['tableSchema'], areaDict[key]['tableName'], areaDict[key]['geom'])
                self.tableDict[tableKey] = areaDict[key]
                self.frameComboBox.addItem(tableKey)
    
    @pyqtSlot(int, name = 'on_frameComboBox_currentIndexChanged')
    def populateCombos(self, idx):
        if self.selectedAbstractDb:
            if idx > 0:
                self.indexComboBox.clear()
                self.inomComboBox.clear()
                self.indexComboBox.addItem(self.tr('Select an attribute from selected table'))
                self.inomComboBox.addItem(self.tr('Select an attribute from selected table'))
                selected = self.tableDict[self.frameComboBox.currentText()]
                attributeList = self.selectedAbstractDb.getAttributesFromTable(selected['tableSchema'], selected['tableName'], typeFilter = ['character', 'character varying', 'text'])
                for attr in attributeList:
                    self.indexComboBox.addItem(attr)
                    self.inomComboBox.addItem(attr)

    def setServerDb(self, abstractDb):
        self.serverAbstractDb = abstractDb
        self.dbTemplateRadioButton.setEnabled(True)
        self.comboBoxPostgis.setServerDb(self.serverAbstractDb)
    
    def setInitialState(self):
        """
        Sets the initial state
        """
        self.prefixVisible = True
        self.sufixVisible = True
        self.dbNameVisible = True
        self.frameGroupBox.hide()
        if not self.serverAbstractDb:
            self.dbTemplateRadioButton.setEnabled(False)
    
    def setPrefixVisible(self, visible):
        """
        Sets if the database prefix should be visible
        """
        if isinstance(visible,bool):
            self.prefixLineEdit.setVisible(visible)
            self.prefixLabel.setVisible(visible)
            self.prefixVisible = visible
    
    def setSufixVisible(self, visible):
        """
        Sets if the database sufix should be visible
        """
        if isinstance(visible,bool):
            self.sufixLineEdit.setVisible(visible)
            self.sufixLabel.setVisible(visible)
            self.sufixVisible = visible
    
    def setDbNameVisible(self, visible):
        """
        Sets if the database name should be visible
        """
        if isinstance(visible,bool):
            self.dbNameLineEdit.setVisible(visible)
            self.dbNameLabel.setVisible(visible)
            self.dbNameVisible = visible
    
    def getVersion(self):
        """
        Get the database version
        """
        return self.versionComboBox.currentText()
    
    def validate(self):
        """
        Validate database name
        """
        errorMsg = ''
        if self.dbNameVisible:
            if self.dbNameLineEdit.text() == '':
                errorMsg += self.tr('Enter a database name!\n')
        if self.mQgsProjectionSelectionWidget.crs().authid() == '':        
            errorMsg += self.tr('Select a coordinate reference system!\n')
        if not self.edgvTemplateRadioButton.isChecked():
            if not self.comboBoxPostgis.currentDb():
                errorMsg += self.tr('Select a template database!\n')
            if self.useFrame:
                if self.frameComboBox.currentIndex() == 0:
                    errorMsg += self.tr('Select a frame layer!\n')
                if self.indexComboBox.currentIndex() == 0:
                    errorMsg += self.tr('Select an index attribute!\n')
                if self.inomComboBox.currentIndex() == 0:
                    errorMsg += self.tr('Select an INOM attribute!\n')
        
        if errorMsg != '':
            QMessageBox.critical(self, self.tr('Critical!'), errorMsg)
            return False
        else:
            return True
    
    @pyqtSlot(bool, name = 'on_edgvTemplateRadioButton_toggled')
    def changeInterfaceState(self, edgvTemplateToggled, hideInterface = True):
        if edgvTemplateToggled:
            self.comboBoxPostgis.setEnabled(False)
            self.frameComboBox.setEnabled(False)
            self.versionComboBox.setEnabled(True)
            self.frameGroupBox.hide()
        else:
            self.comboBoxPostgis.show()
            if self.useFrame:
                self.frameGroupBox.show()
            self.comboBoxPostgis.setEnabled(True)
            self.versionComboBox.setEnabled(False)
        if not isinstance(self.sender(), QRadioButton):
            if hideInterface:
                self.frameGroupBox.hide()
                self.comboBoxPostgis.hide()
                self.dbTemplateRadioButton.hide()
            else:
                self.comboBoxPostgis.show()
                self.dbTemplateRadioButton.show()
    
    def getTemplateName(self):
        if self.edgvTemplateRadioButton.isChecked():
            return None
        else:
            return self.comboBoxPostgis.currentDb()
    
    def getTemplateParameters(self):
        if self.edgvTemplateRadioButton.isChecked():
            paramDict = dict()
            if self.serverAbstractDb:
                paramDict['templateName'] = self.serverAbstractDb.getTemplateName(self.versionComboBox.currentText())
            paramDict['version'] = self.versionComboBox.currentText()
            paramDict['isTemplateEdgv'] = True 
            return paramDict           
        else:
            paramDict = dict()
            paramDict['templateName'] = self.comboBoxPostgis.currentDb()
            paramDict['isTemplateEdgv'] = False
            if self.useFrame:
                selected = self.tableDict[self.frameComboBox.currentText()]
                paramDict['tableSchema'] = selected['tableSchema']
                paramDict['tableName'] = selected['tableName']
                paramDict['geom'] = selected['geom']
                paramDict['miAttr'] = self.indexComboBox.currentText()
                paramDict['inomAttr'] = self.inomComboBox.currentText()
                paramDict['geomType'] = selected['geomType']
            return paramDict
コード例 #6
0
class LayerRegistry(QObject):
    layersChanged = pyqtSignal()

    _instance = None
    _iface = None

    @staticmethod
    def instance():
        if LayerRegistry._instance is None:
            LayerRegistry._instance = LayerRegistry()
        return LayerRegistry._instance

    @staticmethod
    def setIface(iface):
        LayerRegistry._iface = iface

    layers = []

    def __init__(self):
        QObject.__init__(self)
        if LayerRegistry._instance is not None:
            return

        LayerRegistry.layers = self.getAllLayers()
        LayerRegistry._instance = self
        QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers)
        QgsMapLayerRegistry.instance().layerWasAdded.connect(self.layerAdded)
        QgsMapLayerRegistry.instance().layerWillBeRemoved.connect(
            self.removeLayer)

    def getAllLayers(self):
        if LayerRegistry._iface and hasattr(LayerRegistry._iface,
                                            'legendInterface'):
            return LayerRegistry._iface.legendInterface().layers()
        return list(QgsMapLayerRegistry.instance().mapLayers().values())

    def layerAdded(self, layer):
        LayerRegistry.layers.append(layer)
        self.layersChanged.emit()

    def removeLayer(self, layerId):
        LayerRegistry.layers = [
            x for x in LayerRegistry.layers if x.id() != layerId
        ]
        self.layersChanged.emit()

    def removeAllLayers(self):
        LayerRegistry.layers = []
        self.layersChanged.emit()

    @classmethod
    def isRaster(self, layer):
        # only gdal raster layers
        if layer.type() != layer.RasterLayer:
            return False
        if layer.providerType() != 'gdal':
            return False
        return True

    def getRasterLayers(self):
        return list(filter(self.isRaster, LayerRegistry.layers))

    @classmethod
    def isVector(self, layer):
        if layer.type() != layer.VectorLayer:
            return False
        if layer.providerType() != 'ogr':
            return False
        return True

    def getVectorLayers(self):
        return list(filter(self.isVector, LayerRegistry.layers))
コード例 #7
0
class OfflineConverter(QObject):
    progressStopped = pyqtSignal()
    task_progress_updated = pyqtSignal(int, int)
    total_progress_updated = pyqtSignal(int, int, str)

    def __init__(self, project, export_folder, extent, offline_editing):
        super(OfflineConverter, self).__init__(parent=None)
        self.__max_task_progress = 0
        self.__offline_layers = list()
        self.__convertor_progress = None  # for processing feedback
        self.__layers = list()

        self.export_folder = export_folder
        self.extent = extent
        self.offline_editing = offline_editing
        self.project_configuration = ProjectConfiguration(project)

        offline_editing.layerProgressUpdated.connect(
            self.on_offline_editing_next_layer)
        offline_editing.progressModeSet.connect(
            self.on_offline_editing_max_changed)
        offline_editing.progressUpdated.connect(
            self.offline_editing_task_progress)

    def convert(self):
        """
        Convert the project to a portable project.

        :param offline_editing: The offline editing instance
        :param export_folder:   The folder to export to
        """

        project = QgsProject.instance()

        original_project_path = project.fileName()
        project_filename, _ = os.path.splitext(
            os.path.basename(original_project_path))

        # Write a backup of the current project to a temporary file
        project_backup_folder = tempfile.mkdtemp()
        backup_project_path = os.path.join(project_backup_folder,
                                           project_filename + '.qgs')
        QgsProject.instance().write(backup_project_path)

        try:
            if not os.path.exists(self.export_folder):
                os.makedirs(self.export_folder)

            QApplication.setOverrideCursor(Qt.WaitCursor)

            self.__offline_layers = list()
            self.__layers = list(project.mapLayers().values())

            self.total_progress_updated.emit(0, 1,
                                             self.tr('Creating base map'))
            # Create the base map before layers are removed
            if self.project_configuration.create_base_map:
                if 'processing' not in qgis.utils.plugins:
                    QMessageBox.warning(
                        None, self.tr('QFieldSync requires processing'),
                        self.
                        tr('Creating a basemap with QFieldSync requires the processing plugin to be enabled. Processing is not enabled on your system. Please go to Plugins > Manage and Install Plugins and enable processing.'
                           ))
                    return

                if self.project_configuration.base_map_type == ProjectProperties.BaseMapType.SINGLE_LAYER:
                    self.createBaseMapLayer(
                        None, self.project_configuration.base_map_layer,
                        self.project_configuration.base_map_tile_size,
                        self.project_configuration.base_map_mupp)
                else:
                    self.createBaseMapLayer(
                        self.project_configuration.base_map_theme, None,
                        self.project_configuration.base_map_tile_size,
                        self.project_configuration.base_map_mupp)

            # Loop through all layers and copy/remove/offline them
            copied_files = list()
            for current_layer_index, layer in enumerate(self.__layers):
                self.total_progress_updated.emit(
                    current_layer_index - len(self.__offline_layers),
                    len(self.__layers), self.tr('Copying layers'))
                layer_source = LayerSource(layer)

                if layer_source.action == SyncAction.OFFLINE:
                    if self.project_configuration.offline_copy_only_aoi:
                        layer.selectByRect(self.extent)
                    self.__offline_layers.append(layer)
                elif layer_source.action == SyncAction.NO_ACTION:
                    copied_files = layer_source.copy(self.export_folder,
                                                     copied_files)
                elif layer_source.action == SyncAction.KEEP_EXISTENT:
                    layer_source.copy(self.export_folder, copied_files, True)
                elif layer_source.action == SyncAction.REMOVE:
                    project.removeMapLayer(layer)

            project_path = os.path.join(self.export_folder,
                                        project_filename + "_qfield.qgs")

            # save the original project path
            ProjectConfiguration(
                project).original_project_path = original_project_path

            # save the offline project twice so that the offline plugin can "know" that it's a relative path
            QgsProject.instance().write(project_path)
            try:
                # Run the offline plugin for gpkg
                gpkg_filename = "data.gpkg"
                if self.__offline_layers:
                    offline_layer_ids = [l.id() for l in self.__offline_layers]
                    if not self.offline_editing.convertToOfflineProject(
                            self.export_folder, gpkg_filename,
                            offline_layer_ids,
                            self.project_configuration.offline_copy_only_aoi,
                            self.offline_editing.GPKG):
                        raise Exception(
                            self.
                            tr("Error trying to convert layers to offline layers"
                               ))
            except AttributeError:
                # Run the offline plugin for spatialite
                spatialite_filename = "data.sqlite"
                if self.__offline_layers:
                    offline_layer_ids = [l.id() for l in self.__offline_layers]
                    if not self.offline_editing.convertToOfflineProject(
                            self.export_folder, spatialite_filename,
                            offline_layer_ids,
                            self.project_configuration.offline_copy_only_aoi):
                        raise Exception(
                            self.
                            tr("Error trying to convert layers to offline layers"
                               ))

            # Now we have a project state which can be saved as offline project
            QgsProject.instance().write(project_path)
        finally:
            # We need to let the app handle events before loading the next project or QGIS will crash with rasters
            QCoreApplication.processEvents()
            QgsProject.instance().clear()
            QCoreApplication.processEvents()
            QgsProject.instance().read(backup_project_path)
            QgsProject.instance().setFileName(original_project_path)
            QApplication.restoreOverrideCursor()

        self.total_progress_updated.emit(100, 100, self.tr('Finished'))

    def createBaseMapLayer(self, map_theme, layer, tile_size,
                           map_units_per_pixel):
        """
        Create a basemap from map layer(s)

        :param dataPath:             The path where the basemap should be writtent to
        :param extent:               The extent rectangle in which data shall be fetched
        :param map_theme:            The name of the map theme to be rendered
        :param layer:                A layer id to be rendered. Will only be used if map_theme is None.
        :param tile_size:            The extent rectangle in which data shall be fetched
        :param map_units_per_pixel:  Number of map units per pixel (1: 1 m per pixel, 10: 10 m per pixel...)
        """
        extent_string = '{},{},{},{}'.format(self.extent.xMinimum(),
                                             self.extent.xMaximum(),
                                             self.extent.yMinimum(),
                                             self.extent.yMaximum())

        alg = QgsApplication.instance().processingRegistry(
        ).createAlgorithmById('qgis:rasterize')

        params = {
            'EXTENT': extent_string,
            'MAP_THEME': map_theme,
            'LAYER': layer,
            'MAP_UNITS_PER_PIXEL': map_units_per_pixel,
            'TILE_SIZE': tile_size,
            'MAKE_BACKGROUND_TRANSPARENT': False,
            'OUTPUT': os.path.join(self.export_folder, 'basemap.gpkg')
        }

        feedback = QgsProcessingFeedback()
        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())

        results, ok = alg.run(params, context, feedback)

        new_layer = QgsRasterLayer(results['OUTPUT'], self.tr('Basemap'))

        resample_filter = new_layer.resampleFilter()
        resample_filter.setZoomedInResampler(QgsCubicRasterResampler())
        resample_filter.setZoomedOutResampler(QgsBilinearRasterResampler())
        self.project_configuration.project.addMapLayer(new_layer, False)
        layer_tree = QgsProject.instance().layerTreeRoot()
        layer_tree.insertLayer(len(layer_tree.children()), new_layer)

    @pyqtSlot(int, int)
    def on_offline_editing_next_layer(self, layer_index, layer_count):
        msg = self.tr(u'Packaging layer {layer_name}').format(
            layer_name=self.__offline_layers[layer_index - 1].name())
        self.total_progress_updated.emit(layer_index, layer_count, msg)

    @pyqtSlot('QgsOfflineEditing::ProgressMode', int)
    def on_offline_editing_max_changed(self, _, mode_count):
        self.__max_task_progress = mode_count

    @pyqtSlot(int)
    def offline_editing_task_progress(self, progress):
        self.task_progress_updated.emit(progress, self.__max_task_progress)

    def convertorProcessingProgress(self):
        """
        Will create a new progress object for processing to get feedback from the basemap
        algorithm.
        """
        class ConverterProgress(QObject):
            progress_updated = pyqtSignal(int, int)

            def __init__(self):
                QObject.__init__(self)

            def error(self, msg):
                pass

            def setText(self, msg):
                pass

            def setPercentage(self, i):
                self.progress_updated.emit(i, 100)
                QCoreApplication.processEvents()

            def setInfo(self, msg):
                pass

            def setCommand(self, msg):
                pass

            def setDebugInfo(self, msg):
                pass

            def setConsoleInfo(self, msg):
                pass

            def close(self):
                pass

        if not self.__convertor_progress:
            self.__convertor_progress = ConverterProgress()
            self.__convertor_progress.progress_updated.connect(
                self.task_progress_updated)

        return self.__convertor_progress
コード例 #8
0
class Generator(QObject):
    """Builds Model Baker objects from data extracted from databases."""

    stdout = pyqtSignal(str)
    new_message = pyqtSignal(int, str)

    def __init__(self,
                 tool,
                 uri,
                 inheritance,
                 schema=None,
                 pg_estimated_metadata=False,
                 parent=None,
                 mgmt_uri=None):
        """
        Creates a new Generator objects.
        :param uri: The uri that should be used in the resulting project. If authcfg is used, make sure the mgmt_uri is set as well.
        :param mgmt_uri: The uri that should be used to create schemas, tables and query meta information. Does not support authcfg.
        """
        QObject.__init__(self, parent)
        self.tool = tool
        self.uri = uri
        self.mgmt_uri = mgmt_uri
        self.inheritance = inheritance
        self.schema = schema or None
        self.pg_estimated_metadata = pg_estimated_metadata

        self.db_simple_factory = DbSimpleFactory()
        db_factory = self.db_simple_factory.create_factory(self.tool)
        self._db_connector = db_factory.get_db_connector(
            mgmt_uri or uri, schema)
        self._db_connector.stdout.connect(self.print_info)
        self._db_connector.new_message.connect(self.append_print_message)

        self._additional_ignored_layers = [
        ]  # List of layers to ignore set by 3rd parties

        self.collected_print_messages = []

    def print_info(self, text):
        self.stdout.emit(text)

    def print_messages(self):
        for message in self.collected_print_messages:
            self.new_message.emit(message["level"], message["text"])
        self.collected_print_messages.clear()

    def append_print_message(self, level, text):
        message = {'level': level, 'text': text}

        if message not in self.collected_print_messages:
            self.collected_print_messages.append(message)

    def layers(self, filter_layer_list=[]):
        ignored_layers = self.get_ignored_layers()
        tables_info = self.get_tables_info()
        layers = list()

        db_factory = self.db_simple_factory.create_factory(self.tool)

        layer_uri = db_factory.get_layer_uri(self.uri)
        layer_uri.pg_estimated_metadata = self.pg_estimated_metadata

        for record in tables_info:
            # When in PostGIS mode, leaving schema blank should load tables from
            # all schemas, except the ignored ones
            if self.schema:
                if record['schemaname'] != self.schema:
                    continue

            if ignored_layers and record['tablename'] in ignored_layers:
                continue

            if filter_layer_list and record[
                    'tablename'] not in filter_layer_list:
                continue

            is_domain = record['kind_settings'] == 'ENUM' or record[
                'kind_settings'] == 'CATALOGUE' if 'kind_settings' in record else False
            is_attribute = bool(record['attribute_name']
                                ) if 'attribute_name' in record else False
            is_structure = record[
                'kind_settings'] == 'STRUCTURE' if 'kind_settings' in record else False
            is_nmrel = record[
                'kind_settings'] == 'ASSOCIATION' if 'kind_settings' in record else False

            alias = record['table_alias'] if 'table_alias' in record else None
            if not alias:
                if is_domain and is_attribute:
                    short_name = record['ili_name'].split(
                        '.')[-2] + '_' + record['ili_name'].split(
                            '.')[-1] if 'ili_name' in record else ''
                else:
                    short_name = record['ili_name'].split(
                        '.')[-1] if 'ili_name' in record else ''
                alias = short_name

            display_expression = ''
            if 'ili_name' in record:
                meta_attrs = self.get_meta_attrs(record['ili_name'])
                for attr_record in meta_attrs:
                    if attr_record['attr_name'] == 'dispExpression':
                        display_expression = attr_record['attr_value']

            coord_decimals = record[
                'coord_decimals'] if 'coord_decimals' in record else None
            coordinate_precision = None
            if coord_decimals:
                coordinate_precision = 1 / (10**coord_decimals)

            layer = Layer(
                layer_uri.provider, layer_uri.get_data_source_uri(record),
                record['tablename'], record['srid'],
                record['extent'] if 'extent' in record else None,
                record['geometry_column'],
                QgsWkbTypes.parseType(record['type']) or QgsWkbTypes.Unknown,
                alias, is_domain, is_structure, is_nmrel, display_expression,
                coordinate_precision)

            # Configure fields for current table
            fields_info = self.get_fields_info(record['tablename'])
            min_max_info = self.get_min_max_info(record['tablename'])
            value_map_info = self.get_value_map_info(record['tablename'])
            re_iliname = re.compile(r'.*\.(.*)$')

            for fielddef in fields_info:
                column_name = fielddef['column_name']
                fully_qualified_name = fielddef[
                    'fully_qualified_name'] if 'fully_qualified_name' in fielddef else None
                m = re_iliname.match(
                    fully_qualified_name) if fully_qualified_name else None

                alias = None
                if 'column_alias' in fielddef:
                    alias = fielddef['column_alias']
                if m and not alias:
                    alias = m.group(1)

                field = Field(column_name)
                field.alias = alias

                # Should we hide the field?
                hide_attribute = False

                if 'fully_qualified_name' in fielddef:
                    fully_qualified_name = fielddef['fully_qualified_name']
                    if fully_qualified_name:
                        meta_attrs_column = self.get_meta_attrs(
                            fully_qualified_name)

                        for attr_record in meta_attrs_column:
                            if attr_record['attr_name'] == 'hidden':
                                if attr_record['attr_value'] == 'True':
                                    hide_attribute = True
                                    break

                if column_name in IGNORED_FIELDNAMES:
                    hide_attribute = True

                field.hidden = hide_attribute

                if column_name in READONLY_FIELDNAMES:
                    field.read_only = True

                if column_name in min_max_info:
                    field.widget = 'Range'
                    field.widget_config['Min'] = min_max_info[column_name][0]
                    field.widget_config['Max'] = min_max_info[column_name][1]
                    if 'numeric_scale' in fielddef:
                        field.widget_config['Step'] = pow(
                            10, -1 * fielddef['numeric_scale'])
                    # field.widget_config['Suffix'] = fielddef['unit'] if 'unit' in fielddef else ''
                    if 'unit' in fielddef and fielddef['unit'] is not None:
                        field.alias = '{alias} [{unit}]'.format(
                            alias=alias or column_name, unit=fielddef['unit'])

                if column_name in value_map_info:
                    field.widget = 'ValueMap'
                    field.widget_config['map'] = [{
                        val: val
                    } for val in value_map_info[column_name]]

                if 'texttype' in fielddef and fielddef['texttype'] == 'MTEXT':
                    field.widget = 'TextEdit'
                    field.widget_config['IsMultiline'] = True

                data_type = self._db_connector.map_data_types(
                    fielddef['data_type'])
                if 'time' in data_type or 'date' in data_type:
                    field.widget = 'DateTime'
                    field.widget_config['calendar_popup'] = True

                    dateFormat = QLocale(
                        QgsApplication.instance().locale()).dateFormat(
                            QLocale.ShortFormat)
                    timeFormat = QLocale(
                        QgsApplication.instance().locale()).timeFormat(
                            QLocale.ShortFormat)
                    dateTimeFormat = QLocale(
                        QgsApplication.instance().locale()).dateTimeFormat(
                            QLocale.ShortFormat)

                    if data_type == self._db_connector.QGIS_TIME_TYPE:
                        field.widget_config['display_format'] = timeFormat
                    elif data_type == self._db_connector.QGIS_DATE_TIME_TYPE:
                        field.widget_config['display_format'] = dateTimeFormat
                    elif data_type == self._db_connector.QGIS_DATE_TYPE:
                        field.widget_config['display_format'] = dateFormat

                db_factory.customize_widget_editor(field, data_type)

                if 'default_value_expression' in fielddef:
                    field.default_value_expression = fielddef[
                        'default_value_expression']

                if 'enum_domain' in fielddef and fielddef['enum_domain']:
                    field.enum_domain = fielddef['enum_domain']

                layer.fields.append(field)

            layers.append(layer)

        self.print_messages()

        return layers

    def relations(self, layers, filter_layer_list=[]):
        relations_info = self.get_relations_info(filter_layer_list)
        layer_map = dict()
        for layer in layers:
            if layer.name not in layer_map.keys():
                layer_map[layer.name] = list()
            layer_map[layer.name].append(layer)
        relations = list()

        classname_info = [
            record['iliname'] for record in self.get_iliname_dbname_mapping()
        ]

        for record in relations_info:
            if record['referencing_table'] in layer_map.keys(
            ) and record['referenced_table'] in layer_map.keys():
                for referencing_layer in layer_map[
                        record['referencing_table']]:
                    for referenced_layer in layer_map[
                            record['referenced_table']]:
                        relation = Relation()
                        relation.referencing_layer = referencing_layer
                        relation.referenced_layer = referenced_layer
                        relation.referencing_field = record[
                            'referencing_column']
                        relation.referenced_field = record['referenced_column']
                        relation.name = record['constraint_name']
                        relation.strength = QgsRelation.Composition if 'strength' in record and record[
                            'strength'] == 'COMPOSITE' else QgsRelation.Association

                        # For domain-class relations, if we have an extended domain, get its child name
                        child_name = None
                        if referenced_layer.is_domain:
                            # Get child name (if domain is extended)
                            fields = [
                                field for field in referencing_layer.fields
                                if field.name == record['referencing_column']
                            ]
                            if fields:
                                field = fields[0]
                                if field.enum_domain and field.enum_domain not in classname_info:
                                    child_name = field.enum_domain
                        relation.child_domain_name = child_name

                        relations.append(relation)

        # Create the bags_of_enum structure
        bags_of_info = self.get_bags_of_info()
        bags_of_enum = {}
        for record in bags_of_info:
            for layer in layers:
                if record['current_layer_name'] == layer.name:
                    new_item_list = [
                        layer, record['cardinality_min'] + '..' +
                        record['cardinality_max'],
                        layer_map[record['target_layer_name']][0],
                        self._db_connector.tid, self._db_connector.dispName
                    ]
                    unique_current_layer_name = '{}_{}'.format(
                        record['current_layer_name'], layer.geometry_column)
                    if unique_current_layer_name in bags_of_enum.keys():
                        bags_of_enum[unique_current_layer_name][
                            record['attribute']] = new_item_list
                    else:
                        bags_of_enum[unique_current_layer_name] = {
                            record['attribute']: new_item_list
                        }

        return (relations, bags_of_enum)

    def legend(self, layers, ignore_node_names=None):
        legend = LegendGroup(QCoreApplication.translate('LegendGroup', 'root'),
                             ignore_node_names=ignore_node_names)
        tables = LegendGroup(
            QCoreApplication.translate('LegendGroup', 'tables'))
        domains = LegendGroup(
            QCoreApplication.translate('LegendGroup', 'domains'), False)

        point_layers = []
        line_layers = []
        polygon_layers = []

        for layer in layers:
            if layer.geometry_column:
                geometry_type = QgsWkbTypes.geometryType(layer.wkb_type)
                if geometry_type == QgsWkbTypes.PointGeometry:
                    point_layers.append(layer)
                elif geometry_type == QgsWkbTypes.LineGeometry:
                    line_layers.append(layer)
                elif geometry_type == QgsWkbTypes.PolygonGeometry:
                    polygon_layers.append(layer)
            else:
                if layer.is_domain:
                    domains.append(layer)
                else:
                    tables.append(layer)

        for l in polygon_layers:
            legend.append(l)
        for l in line_layers:
            legend.append(l)
        for l in point_layers:
            legend.append(l)

        if not tables.is_empty():
            legend.append(tables)
        if not domains.is_empty():
            legend.append(domains)

        return legend

    def db_or_schema_exists(self):
        return self._db_connector.db_or_schema_exists()

    def metadata_exists(self):
        return self._db_connector.metadata_exists()

    def set_additional_ignored_layers(self, layer_list):
        self._additional_ignored_layers = layer_list

    def get_ignored_layers(self):
        return self._db_connector.get_ignored_layers(
        ) + self._additional_ignored_layers

    def get_tables_info(self):
        return self._db_connector.get_tables_info()

    def get_meta_attrs_info(self):
        return self._db_connector.get_meta_attrs_info()

    def get_meta_attrs(self, ili_name):
        return self._db_connector.get_meta_attrs(ili_name)

    def get_fields_info(self, table_name):
        return self._db_connector.get_fields_info(table_name)

    def get_tables_info_without_ignored_tables(self):
        tables_info = self.get_tables_info()
        ignored_layers = self.get_ignored_layers()
        new_tables_info = []
        for record in tables_info:
            if self.schema:
                if record['schemaname'] != self.schema:
                    continue

            if ignored_layers and record['tablename'] in ignored_layers:
                continue

            new_tables_info.append(record)

        return new_tables_info

    def get_min_max_info(self, table_name):
        return self._db_connector.get_min_max_info(table_name)

    def get_value_map_info(self, table_name):
        return self._db_connector.get_value_map_info(table_name)

    def get_relations_info(self, filter_layer_list=[]):
        return self._db_connector.get_relations_info(filter_layer_list)

    def get_bags_of_info(self):
        return self._db_connector.get_bags_of_info()

    def get_iliname_dbname_mapping(self):
        return self._db_connector.get_iliname_dbname_mapping()
コード例 #9
0
class FieldDataCapture(QObject):
    total_progress_updated = pyqtSignal(int)  # percentage

    def __init__(self):
        QObject.__init__(self)
        self.logger = Logger()
        self.app = AppInterface()

    def convert_to_offline(self, db, surveyor_expression_dict, export_dir):
        sys.path.append(PLUGINS_DIR)
        from qfieldsync.core.layer import LayerSource, SyncAction
        from qfieldsync.core.offline_converter import OfflineConverter
        from qfieldsync.core.project import ProjectConfiguration

        project = QgsProject.instance()
        extent = QgsRectangle()
        offline_editing = QgsOfflineEditing()

        # Configure project
        project_configuration = ProjectConfiguration(project)
        project_configuration.create_base_map = False
        project_configuration.offline_copy_only_aoi = False
        project_configuration.use_layer_selection = True

        # Layer config
        layer_sync_action = LayerConfig.get_field_data_capture_layer_config(
            db.names)

        total_projects = len(surveyor_expression_dict)
        current_progress = 0

        for surveyor, layer_config in surveyor_expression_dict.items():
            export_folder = os.path.join(export_dir, surveyor)

            # Get layers (cannot be done out of this for loop because the project is closed and layers are deleted)
            layers = {
                layer_name: None
                for layer_name, _ in layer_sync_action.items()
            }
            self.app.core.get_layers(db, layers, True)
            if not layers:
                return False, QCoreApplication.translate(
                    "FieldDataCapture",
                    "At least one layer could not be found.")

            # Configure layers
            for layer_name, layer in layers.items():
                layer_source = LayerSource(layer)
                layer_source.action = layer_sync_action[layer_name]
                if layer_name in layer_config:
                    layer_source.select_expression = layer_config[layer_name]
                layer_source.apply()

            offline_converter = OfflineConverter(project, export_folder,
                                                 extent, offline_editing)
            offline_converter.convert()
            offline_editing.layerProgressUpdated.disconnect(
                offline_converter.on_offline_editing_next_layer)
            offline_editing.progressModeSet.disconnect(
                offline_converter.on_offline_editing_max_changed)
            offline_editing.progressUpdated.disconnect(
                offline_converter.offline_editing_task_progress)

            current_progress += 1
            self.total_progress_updated.emit(
                int(100 * current_progress / total_projects))

        return True, QCoreApplication.translate(
            "FieldDataCapture",
            "{count} offline projects have been successfully created in <a href='file:///{normalized_path}'>{path}</a>!"
        ).format(count=total_projects,
                 normalized_path=normalize_local_url(export_dir),
                 path=export_dir)
コード例 #10
0
ファイル: NumberInputPanel.py プロジェクト: vagvaf/QGIS
class NumberInputPanel(NUMBER_BASE, NUMBER_WIDGET):
    """
    Number input panel for use outside the modeller - this input panel
    contains a user friendly spin box for entering values. It also
    allows expressions to be evaluated, but these expressions are evaluated
    immediately after entry and are not stored anywhere.
    """

    hasChanged = pyqtSignal()

    def __init__(self, param):
        super(NumberInputPanel, self).__init__(None)
        self.setupUi(self)

        self.spnValue.setExpressionsEnabled(True)

        self.param = param
        if self.param.dataType() == QgsProcessingParameterNumber.Integer:
            self.spnValue.setDecimals(0)
        else:
            # Guess reasonable step value
            if self.param.maximum() is not None and self.param.minimum(
            ) is not None:
                try:
                    self.spnValue.setSingleStep(
                        self.calculateStep(float(self.param.minimum()),
                                           float(self.param.maximum())))
                except:
                    pass

        if self.param.maximum() is not None:
            self.spnValue.setMaximum(self.param.maximum())
        else:
            self.spnValue.setMaximum(999999999)
        if self.param.minimum() is not None:
            self.spnValue.setMinimum(self.param.minimum())
        else:
            self.spnValue.setMinimum(-999999999)

        # set default value
        if param.defaultValue() is not None:
            self.setValue(param.defaultValue())
            try:
                self.spnValue.setClearValue(float(param.defaultValue()))
            except:
                pass
        elif self.param.minimum() is not None:
            try:
                self.setValue(float(self.param.minimum()))
                self.spnValue.setClearValue(float(self.param.minimum()))
            except:
                pass
        else:
            self.setValue(0)
            self.spnValue.setClearValue(0)
        self.btnSelect.setFixedHeight(self.spnValue.height())

        self.btnSelect.clicked.connect(self.showExpressionsBuilder)
        self.spnValue.valueChanged.connect(lambda: self.hasChanged.emit())

    def showExpressionsBuilder(self):
        context = createExpressionContext()
        dlg = QgsExpressionBuilderDialog(None, str(self.spnValue.value()),
                                         self, 'generic', context)

        dlg.setWindowTitle(self.tr('Expression based input'))
        if dlg.exec_() == QDialog.Accepted:
            exp = QgsExpression(dlg.expressionText())
            if not exp.hasParserError():
                try:
                    val = float(exp.evaluate(context))
                    self.setValue(val)
                except:
                    return

    def getValue(self):
        return self.spnValue.value()

    def setValue(self, value):
        try:
            self.spnValue.setValue(float(value))
        except:
            return

    def calculateStep(self, minimum, maximum):
        value_range = maximum - minimum
        if value_range <= 1.0:
            step = value_range / 10.0
            # round to 1 significant figrue
            return round(step, -int(math.floor(math.log10(step))))
        else:
            return 1.0
コード例 #11
0
ファイル: NumberInputPanel.py プロジェクト: vagvaf/QGIS
class ModellerNumberInputPanel(BASE, WIDGET):
    """
    Number input panel for use inside the modeller - this input panel
    is based off the base input panel and includes a text based line input
    for entering values. This allows expressions and other non-numeric
    values to be set, which are later evalauted to numbers when the model
    is run.
    """

    hasChanged = pyqtSignal()

    def __init__(self, param, modelParametersDialog):
        super(ModellerNumberInputPanel, self).__init__(None)
        self.setupUi(self)

        self.param = param
        self.modelParametersDialog = modelParametersDialog
        if param.defaultValue():
            self.setValue(param.defaultValue())
        self.btnSelect.clicked.connect(self.showExpressionsBuilder)
        self.leText.textChanged.connect(lambda: self.hasChanged.emit())

    def showExpressionsBuilder(self):
        context = createExpressionContext()
        dlg = QgsExpressionBuilderDialog(None, str(self.leText.text()), self,
                                         'generic', context)

        context.popScope()
        values = self.modelParametersDialog.getAvailableValuesOfType(
            ParameterNumber, OutputNumber)
        variables = {}
        for value in values:
            if isinstance(value, ValueFromInput):
                name = value.name
                element = self.modelParametersDialog.model.inputs[name].param
                desc = element.description
            else:
                name = "%s_%s" % (value.alg, value.output)
                alg = self.modelParametersDialog.model.algs[value.alg]
                out = alg.algorithm.getOutputFromName(value.output)
                desc = self.tr("Output '{0}' from algorithm '{1}'").format(
                    out.description(), alg.description)
            variables[name] = desc
        values = self.modelParametersDialog.getAvailableValuesOfType(
            ParameterVector, OutputVector)
        values.extend(
            self.modelParametersDialog.getAvailableValuesOfType(
                ParameterRaster, OutputRaster))
        for value in values:
            if isinstance(value, ValueFromInput):
                name = value.name
                element = self.modelParametersDialog.model.inputs[name].param
                desc = element.description
            else:
                name = "%s_%s" % (value.alg, value.output)
                alg = self.modelParametersDialog.model.algs[value.alg]
                element = alg.algorithm.getOutputFromName(value.output)
                desc = self.tr("Output '{0}' from algorithm '{1}'").format(
                    element.description(), alg.description)
            variables['%s_minx' %
                      name] = self.tr("Minimum X of {0}").format(desc)
            variables['%s_miny' %
                      name] = self.tr("Minimum Y of {0}").format(desc)
            variables['%s_maxx' %
                      name] = self.tr("Maximum X of {0}").format(desc)
            variables['%s_maxy' %
                      name] = self.tr("Maximum Y of {0}").format(desc)
            if isinstance(element, (ParameterRaster, OutputRaster)):
                variables['%s_min' %
                          name] = self.tr("Minimum value of {0}").format(desc)
                variables['%s_max' %
                          name] = self.tr("Maximum value of {0}").format(desc)
                variables['%s_avg' %
                          name] = self.tr("Mean value of {0}").format(desc)
                variables['%s_stddev' % name] = self.tr(
                    "Standard deviation of {0}").format(desc)
        for variable, desc in variables.items():
            dlg.expressionBuilder().registerItem("Modeler",
                                                 variable,
                                                 "@" + variable,
                                                 desc,
                                                 highlightedItem=True)

        dlg.setWindowTitle(self.tr('Expression based input'))
        if dlg.exec_() == QDialog.Accepted:
            exp = QgsExpression(dlg.expressionText())
            if not exp.hasParserError():
                self.setValue(dlg.expressionText())

    def getValue(self):
        value = self.leText.text()
        values = []
        for param in self.modelParametersDialog.model.parameters:
            if isinstance(param, ParameterNumber):
                if "@" + param.name() in value:
                    values.append(ValueFromInput(param.name()))
        for alg in list(self.modelParametersDialog.model.algs.values()):
            for out in alg.algorithm.outputs:
                if isinstance(out,
                              OutputNumber) and "@%s_%s" % (alg.name(),
                                                            out.name) in value:
                    values.append(ValueFromOutput(alg.name(), out.name))
        if values:
            return CompoundValue(values, value)
        else:
            return value

    def setValue(self, value):
        self.leText.setText(str(value))
コード例 #12
0
class DlgSqlWindow(QWidget, Ui_Dialog):
    nameChanged = pyqtSignal(str)
    QUERY_HISTORY_LIMIT = 20

    def __init__(self, iface, db, parent=None):
        QWidget.__init__(self, parent)
        self.mainWindow = parent
        self.iface = iface
        self.db = db
        self.dbType = db.connection().typeNameString()
        self.connectionName = db.connection().connectionName()
        self.filter = ""
        self.modelAsync = None
        self.allowMultiColumnPk = isinstance(
            db, PGDatabase
        )  # at the moment only PostgreSQL allows a primary key to span multiple columns, SpatiaLite doesn't
        self.aliasSubQuery = isinstance(
            db,
            PGDatabase)  # only PostgreSQL requires subqueries to be aliases
        self.setupUi(self)
        self.setWindowTitle(
            self.tr(u"{0} - {1} [{2}]").format(self.windowTitle(),
                                               self.connectionName,
                                               self.dbType))

        self.defaultLayerName = self.tr('QueryLayer')

        if self.allowMultiColumnPk:
            self.uniqueColumnCheck.setText(
                self.tr("Column(s) with unique values"))
        else:
            self.uniqueColumnCheck.setText(
                self.tr("Column with unique values"))

        self.editSql.setFocus()
        self.editSql.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.editSql.setMarginVisible(True)
        self.initCompleter()

        settings = QgsSettings()
        self.history = settings.value('DB_Manager/queryHistory/' + self.dbType,
                                      {self.connectionName: []})
        if self.connectionName not in self.history:
            self.history[self.connectionName] = []

        self.queryHistoryWidget.setVisible(False)
        self.queryHistoryTableWidget.verticalHeader().hide()
        self.queryHistoryTableWidget.doubleClicked.connect(
            self.insertQueryInEditor)
        self.populateQueryHistory()
        self.btnQueryHistory.toggled.connect(self.showHideQueryHistory)

        self.btnCancel.setEnabled(False)
        self.btnCancel.clicked.connect(self.executeSqlCanceled)
        self.btnCancel.setShortcut(QKeySequence.Cancel)
        self.progressBar.setEnabled(False)
        self.progressBar.setRange(0, 100)
        self.progressBar.setValue(0)
        self.progressBar.setFormat("")
        self.progressBar.setAlignment(Qt.AlignCenter)

        # allow copying results
        copyAction = QAction("copy", self)
        self.viewResult.addAction(copyAction)
        copyAction.setShortcuts(QKeySequence.Copy)

        copyAction.triggered.connect(self.copySelectedResults)

        self.btnExecute.clicked.connect(self.executeSql)
        self.btnSetFilter.clicked.connect(self.setFilter)
        self.btnClear.clicked.connect(self.clearSql)

        self.presetStore.clicked.connect(self.storePreset)
        self.presetSaveAsFile.clicked.connect(self.saveAsFilePreset)
        self.presetLoadFile.clicked.connect(self.loadFilePreset)
        self.presetDelete.clicked.connect(self.deletePreset)
        self.presetCombo.activated[str].connect(self.loadPreset)
        self.presetCombo.activated[str].connect(self.presetName.setText)

        self.updatePresetsCombobox()

        self.geomCombo.setEditable(True)
        self.geomCombo.lineEdit().setReadOnly(True)

        self.uniqueCombo.setEditable(True)
        self.uniqueCombo.lineEdit().setReadOnly(True)
        self.uniqueModel = QStandardItemModel(self.uniqueCombo)
        self.uniqueCombo.setModel(self.uniqueModel)
        if self.allowMultiColumnPk:
            self.uniqueCombo.setItemDelegate(QStyledItemDelegate())
            self.uniqueModel.itemChanged.connect(
                self.uniqueChanged)  # react to the (un)checking of an item
            self.uniqueCombo.lineEdit().textChanged.connect(
                self.uniqueTextChanged
            )  # there are other events that change the displayed text and some of them can not be caught directly

        # hide the load query as layer if feature is not supported
        self._loadAsLayerAvailable = self.db.connector.hasCustomQuerySupport()
        self.loadAsLayerGroup.setVisible(self._loadAsLayerAvailable)
        if self._loadAsLayerAvailable:
            self.layerTypeWidget.hide()  # show if load as raster is supported
            self.loadLayerBtn.clicked.connect(self.loadSqlLayer)
            self.getColumnsBtn.clicked.connect(self.fillColumnCombos)
            self.loadAsLayerGroup.toggled.connect(self.loadAsLayerToggled)
            self.loadAsLayerToggled(False)

        self._createViewAvailable = self.db.connector.hasCreateSpatialViewSupport(
        )
        self.btnCreateView.setVisible(self._createViewAvailable)
        if self._createViewAvailable:
            self.btnCreateView.clicked.connect(self.createView)

        self.queryBuilderFirst = True
        self.queryBuilderBtn.setIcon(QIcon(":/db_manager/icons/sql.gif"))
        self.queryBuilderBtn.clicked.connect(self.displayQueryBuilder)

        self.presetName.textChanged.connect(self.nameChanged)

    def insertQueryInEditor(self, item):
        sql = item.data(Qt.DisplayRole)
        self.editSql.insertText(sql)

    def showHideQueryHistory(self, visible):
        self.queryHistoryWidget.setVisible(visible)

    def populateQueryHistory(self):
        self.queryHistoryTableWidget.clearContents()
        self.queryHistoryTableWidget.setRowCount(0)
        dictlist = self.history[self.connectionName]

        if not dictlist:
            return

        for i in range(len(dictlist)):
            self.queryHistoryTableWidget.insertRow(0)
            queryItem = QTableWidgetItem(dictlist[i]['query'])
            rowsItem = QTableWidgetItem(str(dictlist[i]['rows']))
            durationItem = QTableWidgetItem(str(dictlist[i]['secs']))
            self.queryHistoryTableWidget.setItem(0, 0, queryItem)
            self.queryHistoryTableWidget.setItem(0, 1, rowsItem)
            self.queryHistoryTableWidget.setItem(0, 2, durationItem)

        self.queryHistoryTableWidget.resizeColumnsToContents()
        self.queryHistoryTableWidget.resizeRowsToContents()

    def writeQueryHistory(self, sql, affectedRows, secs):
        if len(self.history[self.connectionName]) >= self.QUERY_HISTORY_LIMIT:
            self.history[self.connectionName].pop(0)

        settings = QgsSettings()
        self.history[self.connectionName].append({
            'query': sql,
            'rows': affectedRows,
            'secs': secs
        })
        settings.setValue('DB_Manager/queryHistory/' + self.dbType,
                          self.history)

        self.populateQueryHistory()

    def getQueryHash(self, name):
        return 'q%s' % md5(name.encode('utf8')).hexdigest()

    def updatePresetsCombobox(self):
        self.presetCombo.clear()

        names = []
        entries = QgsProject.instance().subkeyList('DBManager', 'savedQueries')
        for entry in entries:
            name = QgsProject.instance().readEntry(
                'DBManager', 'savedQueries/' + entry + '/name')[0]
            names.append(name)

        for name in sorted(names):
            self.presetCombo.addItem(name)
        self.presetCombo.setCurrentIndex(-1)

    def storePreset(self):
        query = self._getSqlQuery()
        if query == "":
            return
        name = str(self.presetName.text())
        QgsProject.instance().writeEntry(
            'DBManager', 'savedQueries/' + self.getQueryHash(name) + '/name',
            name)
        QgsProject.instance().writeEntry(
            'DBManager', 'savedQueries/' + self.getQueryHash(name) + '/query',
            query)
        index = self.presetCombo.findText(name)
        if index == -1:
            self.presetCombo.addItem(name)
            self.presetCombo.setCurrentIndex(self.presetCombo.count() - 1)
        else:
            self.presetCombo.setCurrentIndex(index)

    def saveAsFilePreset(self):
        settings = QgsSettings()
        lastDir = settings.value('DB_Manager/lastDirSQLFIle', "")

        query = self._getSqlQuery()
        if query == "":
            return

        filename, _ = QFileDialog.getSaveFileName(
            self, self.tr('Save SQL Query'), lastDir,
            self.tr("SQL File (*.sql, *.SQL)"))

        if filename:
            if not filename.lower().endswith('.sql'):
                filename += ".sql"

            with open(filename, 'w') as f:
                f.write(query)
                lastDir = os.path.dirname(filename)
                settings.setValue('DB_Manager/lastDirSQLFile', lastDir)

    def loadFilePreset(self):
        settings = QgsSettings()
        lastDir = settings.value('DB_Manager/lastDirSQLFIle', "")

        filename, _ = QFileDialog.getOpenFileName(
            self, self.tr("Load SQL Query"), lastDir,
            self.tr("SQL File (*.sql, *.SQL)"))

        if filename:
            with open(filename, 'r') as f:
                self.editSql.clear()
                for line in f:
                    self.editSql.insertText(line)
                lastDir = os.path.dirname(filename)
                settings.setValue('DB_Manager/lastDirSQLFile', lastDir)

    def deletePreset(self):
        name = self.presetCombo.currentText()
        QgsProject.instance().removeEntry(
            'DBManager', 'savedQueries/' + self.getQueryHash(name))
        self.presetCombo.removeItem(self.presetCombo.findText(name))
        self.presetCombo.setCurrentIndex(-1)

    def loadPreset(self, name):
        query = QgsProject.instance().readEntry(
            'DBManager',
            'savedQueries/' + self.getQueryHash(name) + '/query')[0]
        self.editSql.setText(query)

    def loadAsLayerToggled(self, checked):
        self.loadAsLayerGroup.setChecked(checked)
        self.loadAsLayerWidget.setVisible(checked)
        if checked:
            self.fillColumnCombos()

    def clearSql(self):
        self.editSql.clear()
        self.editSql.setFocus()
        self.filter = ""

    def updateUiWhileSqlExecution(self, status):
        if status:
            for i in range(0, self.mainWindow.tabs.count()):
                if i != self.mainWindow.tabs.currentIndex():
                    self.mainWindow.tabs.setTabEnabled(i, False)

            self.mainWindow.menuBar.setEnabled(False)
            self.mainWindow.toolBar.setEnabled(False)
            self.mainWindow.tree.setEnabled(False)

            for w in self.findChildren(QWidget):
                w.setEnabled(False)

            self.btnCancel.setEnabled(True)
            self.progressBar.setEnabled(True)
            self.progressBar.setRange(0, 0)
        else:
            for i in range(0, self.mainWindow.tabs.count()):
                if i != self.mainWindow.tabs.currentIndex():
                    self.mainWindow.tabs.setTabEnabled(i, True)

            self.mainWindow.refreshTabs()
            self.mainWindow.menuBar.setEnabled(True)
            self.mainWindow.toolBar.setEnabled(True)
            self.mainWindow.tree.setEnabled(True)

            for w in self.findChildren(QWidget):
                w.setEnabled(True)

            self.btnCancel.setEnabled(False)
            self.progressBar.setRange(0, 100)
            self.progressBar.setEnabled(False)

    def executeSqlCanceled(self):
        self.btnCancel.setEnabled(False)
        self.modelAsync.cancel()

    def executeSqlCompleted(self):
        self.updateUiWhileSqlExecution(False)

        with OverrideCursor(Qt.WaitCursor):
            if self.modelAsync.task.status() == QgsTask.Complete:
                model = self.modelAsync.model
                quotedCols = []

                self.viewResult.setModel(model)
                self.lblResult.setText(
                    self.tr("{0} rows, {1:.3f} seconds").format(
                        model.affectedRows(), model.secs()))
                cols = self.viewResult.model().columnNames()
                for col in cols:
                    quotedCols.append(self.db.connector.quoteId(col))

                self.setColumnCombos(cols, quotedCols)

                self.writeQueryHistory(self.modelAsync.task.sql,
                                       model.affectedRows(), model.secs())
                self.update()
            elif not self.modelAsync.canceled:
                DlgDbError.showError(self.modelAsync.error, self)
                self.uniqueModel.clear()
                self.geomCombo.clear()

    def executeSql(self):

        sql = self._getExecutableSqlQuery()
        if sql == "":
            return

        # delete the old model
        old_model = self.viewResult.model()
        self.viewResult.setModel(None)
        if old_model:
            old_model.deleteLater()

        try:
            self.modelAsync = self.db.sqlResultModelAsync(sql, self)
            self.modelAsync.done.connect(self.executeSqlCompleted)
            self.updateUiWhileSqlExecution(True)
            QgsApplication.taskManager().addTask(self.modelAsync.task)
        except Exception as e:
            DlgDbError.showError(e, self)
            self.uniqueModel.clear()
            self.geomCombo.clear()
            return

    def _getSqlLayer(self, _filter):
        hasUniqueField = self.uniqueColumnCheck.checkState() == Qt.Checked
        if hasUniqueField:
            if self.allowMultiColumnPk:
                checkedCols = []
                for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
                    if item.checkState() == Qt.Checked:
                        checkedCols.append(item.data())
                uniqueFieldName = ",".join(checkedCols)
            elif self.uniqueCombo.currentIndex() >= 0:
                uniqueFieldName = self.uniqueModel.item(
                    self.uniqueCombo.currentIndex()).data()
            else:
                uniqueFieldName = None
        else:
            uniqueFieldName = None
        hasGeomCol = self.hasGeometryCol.checkState() == Qt.Checked
        if hasGeomCol:
            geomFieldName = self.geomCombo.currentText()
        else:
            geomFieldName = None

        query = self._getSqlExecutableQuery()
        if query == "":
            return None

        # remove a trailing ';' from query if present
        if query.strip().endswith(';'):
            query = query.strip()[:-1]

        from qgis.core import QgsMapLayer

        layerType = QgsMapLayer.VectorLayer if self.vectorRadio.isChecked(
        ) else QgsMapLayer.RasterLayer

        # get a new layer name
        names = []
        for layer in list(QgsProject.instance().mapLayers().values()):
            names.append(layer.name())

        layerName = self.layerNameEdit.text()
        if layerName == "":
            layerName = self.defaultLayerName
        newLayerName = layerName
        index = 1
        while newLayerName in names:
            index += 1
            newLayerName = u"%s_%d" % (layerName, index)

        # create the layer
        layer = self.db.toSqlLayer(query, geomFieldName, uniqueFieldName,
                                   newLayerName, layerType,
                                   self.avoidSelectById.isChecked(), _filter)
        if layer.isValid():
            return layer
        else:
            e = BaseError(
                self.
                tr("There was an error creating the SQL layer, please check the logs for further information."
                   ))
            DlgDbError.showError(e, self)
            return None

    def loadSqlLayer(self):
        with OverrideCursor(Qt.WaitCursor):
            layer = self._getSqlLayer(self.filter)
            if layer is None:
                return

            QgsProject.instance().addMapLayers([layer], True)

    def fillColumnCombos(self):
        query = self._getExecutableSqlQuery()
        if query == "":
            return

        with OverrideCursor(Qt.WaitCursor):
            # remove a trailing ';' from query if present
            if query.strip().endswith(';'):
                query = query.strip()[:-1]

            # get all the columns
            quotedCols = []
            connector = self.db.connector
            if self.aliasSubQuery:
                # get a new alias
                aliasIndex = 0
                while True:
                    alias = "_subQuery__%d" % aliasIndex
                    escaped = re.compile('\\b("?)' + re.escape(alias) +
                                         '\\1\\b')
                    if not escaped.search(query):
                        break
                    aliasIndex += 1

                sql = u"SELECT * FROM (%s\n) AS %s LIMIT 0" % (
                    str(query), connector.quoteId(alias))
            else:
                sql = u"SELECT * FROM (%s\n) WHERE 1=0" % str(query)

            c = None
            try:
                c = connector._execute(None, sql)
                cols = connector._get_cursor_columns(c)
                for col in cols:
                    quotedCols.append(connector.quoteId(col))

            except BaseError as e:
                DlgDbError.showError(e, self)
                self.uniqueModel.clear()
                self.geomCombo.clear()
                return

            finally:
                if c:
                    c.close()
                    del c

            self.setColumnCombos(cols, quotedCols)

    def setColumnCombos(self, cols, quotedCols):
        # get sensible default columns. do this before sorting in case there's hints in the column order (e.g., id is more likely to be first)
        try:
            defaultGeomCol = next(
                col for col in cols
                if col in ['geom', 'geometry', 'the_geom', 'way'])
        except:
            defaultGeomCol = None
        try:
            defaultUniqueCol = [col for col in cols if 'id' in col][0]
        except:
            defaultUniqueCol = None

        colNames = sorted(zip(cols, quotedCols))
        newItems = []
        uniqueIsFilled = False
        for (col, quotedCol) in colNames:
            item = QStandardItem(col)
            item.setData(quotedCol)
            item.setEnabled(True)
            item.setCheckable(self.allowMultiColumnPk)
            item.setSelectable(not self.allowMultiColumnPk)
            if self.allowMultiColumnPk:
                matchingItems = self.uniqueModel.findItems(col)
                if matchingItems:
                    item.setCheckState(matchingItems[0].checkState())
                    uniqueIsFilled = uniqueIsFilled or matchingItems[
                        0].checkState() == Qt.Checked
                else:
                    item.setCheckState(Qt.Unchecked)
            newItems.append(item)
        if self.allowMultiColumnPk:
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            self.uniqueChanged()
        else:
            previousUniqueColumn = self.uniqueCombo.currentText()
            self.uniqueModel.clear()
            self.uniqueModel.appendColumn(newItems)
            if self.uniqueModel.findItems(previousUniqueColumn):
                self.uniqueCombo.setEditText(previousUniqueColumn)
                uniqueIsFilled = True

        oldGeometryColumn = self.geomCombo.currentText()
        self.geomCombo.clear()
        self.geomCombo.addItems(cols)
        self.geomCombo.setCurrentIndex(
            self.geomCombo.findText(oldGeometryColumn, Qt.MatchExactly))

        # set sensible default columns if the columns are not already set
        try:
            if self.geomCombo.currentIndex() == -1:
                self.geomCombo.setCurrentIndex(cols.index(defaultGeomCol))
        except:
            pass
        items = self.uniqueModel.findItems(defaultUniqueCol)
        if items and not uniqueIsFilled:
            if self.allowMultiColumnPk:
                items[0].setCheckState(Qt.Checked)
            else:
                self.uniqueCombo.setEditText(defaultUniqueCol)

    def copySelectedResults(self):
        if len(self.viewResult.selectedIndexes()) <= 0:
            return
        model = self.viewResult.model()

        # convert to string using tab as separator
        text = model.headerToString("\t")
        for idx in self.viewResult.selectionModel().selectedRows():
            text += "\n" + model.rowToString(idx.row(), "\t")

        QApplication.clipboard().setText(text, QClipboard.Selection)
        QApplication.clipboard().setText(text, QClipboard.Clipboard)

    def initCompleter(self):
        dictionary = None
        if self.db:
            dictionary = self.db.connector.getSqlDictionary()
        if not dictionary:
            # use the generic sql dictionary
            from .sql_dictionary import getSqlDictionary

            dictionary = getSqlDictionary()

        wordlist = []
        for value in dictionary.values():
            wordlist += value  # concat lists
        wordlist = list(set(wordlist))  # remove duplicates

        api = QsciAPIs(self.editSql.lexer())
        for word in wordlist:
            api.add(word)

        api.prepare()
        self.editSql.lexer().setAPIs(api)

    def displayQueryBuilder(self):
        dlg = QueryBuilderDlg(self.iface,
                              self.db,
                              self,
                              reset=self.queryBuilderFirst)
        self.queryBuilderFirst = False
        r = dlg.exec_()
        if r == QDialog.Accepted:
            self.editSql.setText(dlg.query)

    def createView(self):
        name, ok = QInputDialog.getText(None, self.tr("View Name"),
                                        self.tr("View name"))
        if ok:
            try:
                self.db.connector.createSpatialView(
                    name, self._getExecutableSqlQuery())
            except BaseError as e:
                DlgDbError.showError(e, self)

    def _getSqlQuery(self):
        sql = self.editSql.selectedText()
        if len(sql) == 0:
            sql = self.editSql.text()
        return sql

    def _getExecutableSqlQuery(self):
        sql = self._getSqlQuery()

        # Clean it up!
        lines = []
        for line in sql.split('\n'):
            if not line.strip().startswith('--'):
                lines.append(line)
        sql = ' '.join(lines)
        return sql.strip()

    def uniqueChanged(self):
        # when an item is (un)checked, simply trigger an update of the combobox text
        self.uniqueTextChanged(None)

    def uniqueTextChanged(self, text):
        # Whenever there is new text displayed in the combobox, check if it is the correct one and if not, display the correct one.
        checkedItems = []
        for item in self.uniqueModel.findItems("*", Qt.MatchWildcard):
            if item.checkState() == Qt.Checked:
                checkedItems.append(item.text())
        label = ", ".join(checkedItems)
        if text != label:
            self.uniqueCombo.setEditText(label)

    def setFilter(self):
        from qgis.gui import QgsQueryBuilder
        layer = self._getSqlLayer("")
        if not layer:
            return

        dlg = QgsQueryBuilder(layer)
        dlg.setSql(self.filter)
        if dlg.exec_():
            self.filter = dlg.sql()
        layer.deleteLater()
コード例 #13
0
class ModelerDialog(BASE, WIDGET):
    ALG_ITEM = 'ALG_ITEM'
    PROVIDER_ITEM = 'PROVIDER_ITEM'
    GROUP_ITEM = 'GROUP_ITEM'

    NAME_ROLE = Qt.UserRole
    TAG_ROLE = Qt.UserRole + 1
    TYPE_ROLE = Qt.UserRole + 2

    CANVAS_SIZE = 4000

    update_model = pyqtSignal()

    def __init__(self, model=None):
        super().__init__(None)
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setupUi(self)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.centralWidget().layout().insertWidget(0, self.bar)

        try:
            self.setDockOptions(self.dockOptions()
                                | QMainWindow.GroupedDragging)
        except:
            pass

        self.mToolbar.setIconSize(iface.iconSize())
        self.mActionOpen.setIcon(
            QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
        self.mActionSave.setIcon(
            QgsApplication.getThemeIcon('/mActionFileSave.svg'))
        self.mActionSaveAs.setIcon(
            QgsApplication.getThemeIcon('/mActionFileSaveAs.svg'))
        self.mActionZoomActual.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomActual.svg'))
        self.mActionZoomIn.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomIn.svg'))
        self.mActionZoomOut.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomOut.svg'))
        self.mActionExportImage.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg'))
        self.mActionZoomToItems.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg'))
        self.mActionExportPdf.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg'))
        self.mActionExportSvg.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg'))
        #self.mActionExportPython.setIcon(
        #    QgsApplication.getThemeIcon('/mActionSaveAsPython.svg'))
        self.mActionEditHelp.setIcon(
            QgsApplication.getThemeIcon('/mActionEditHelpContent.svg'))
        self.mActionRun.setIcon(
            QgsApplication.getThemeIcon('/mActionStart.svg'))

        self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock)
        self.tabifyDockWidget(self.inputsDock, self.algorithmsDock)
        self.inputsDock.raise_()

        self.zoom = 1

        self.setWindowFlags(Qt.WindowMinimizeButtonHint
                            | Qt.WindowMaximizeButtonHint
                            | Qt.WindowCloseButtonHint)

        settings = QgsSettings()
        self.restoreState(
            settings.value("/Processing/stateModeler", QByteArray()))
        self.restoreGeometry(
            settings.value("/Processing/geometryModeler", QByteArray()))

        self.scene = ModelerScene(self, dialog=self)
        self.scene.setSceneRect(
            QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))

        self.view.setScene(self.scene)
        self.view.setAcceptDrops(True)
        self.view.ensureVisible(0, 0, 10, 10)

        def _dragEnterEvent(event):
            if event.mimeData().hasText():
                event.acceptProposedAction()
            else:
                event.ignore()

        def _dropEvent(event):
            if event.mimeData().hasText():
                itemId = event.mimeData().text()
                if itemId in [
                        param.id() for param in QgsApplication.instance().
                        processingRegistry().parameterTypes()
                ]:
                    self.addInputOfType(itemId, event.pos())
                else:
                    alg = QgsApplication.processingRegistry(
                    ).createAlgorithmById(itemId)
                    if alg is not None:
                        self._addAlgorithm(alg, event.pos())
                event.accept()
            else:
                event.ignore()

        def _dragMoveEvent(event):
            if event.mimeData().hasText():
                event.accept()
            else:
                event.ignore()

        def _wheelEvent(event):
            self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)

            settings = QgsSettings()
            factor = settings.value('/qgis/zoom_favor', 2.0)

            # "Normal" mouse has an angle delta of 120, precision mouses provide data
            # faster, in smaller steps
            factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y())

            if (event.modifiers() == Qt.ControlModifier):
                factor = 1.0 + (factor - 1.0) / 20.0

            if event.angleDelta().y() < 0:
                factor = 1 / factor

            self.view.scale(factor, factor)

        def _enterEvent(e):
            QGraphicsView.enterEvent(self.view, e)
            self.view.viewport().setCursor(Qt.ArrowCursor)

        def _mouseReleaseEvent(e):
            QGraphicsView.mouseReleaseEvent(self.view, e)
            self.view.viewport().setCursor(Qt.ArrowCursor)

        def _mousePressEvent(e):
            if e.button() == Qt.MidButton:
                self.previousMousePos = e.pos()
            else:
                QGraphicsView.mousePressEvent(self.view, e)

        def _mouseMoveEvent(e):
            if e.buttons() == Qt.MidButton:
                offset = self.previousMousePos - e.pos()
                self.previousMousePos = e.pos()

                self.view.verticalScrollBar().setValue(
                    self.view.verticalScrollBar().value() + offset.y())
                self.view.horizontalScrollBar().setValue(
                    self.view.horizontalScrollBar().value() + offset.x())
            else:
                QGraphicsView.mouseMoveEvent(self.view, e)

        self.view.setDragMode(QGraphicsView.ScrollHandDrag)
        self.view.dragEnterEvent = _dragEnterEvent
        self.view.dropEvent = _dropEvent
        self.view.dragMoveEvent = _dragMoveEvent
        self.view.wheelEvent = _wheelEvent
        self.view.enterEvent = _enterEvent
        self.view.mousePressEvent = _mousePressEvent
        self.view.mouseMoveEvent = _mouseMoveEvent

        def _mimeDataInput(items):
            mimeData = QMimeData()
            text = items[0].data(0, Qt.UserRole)
            mimeData.setText(text)
            return mimeData

        self.inputsTree.mimeData = _mimeDataInput

        self.inputsTree.setDragDropMode(QTreeWidget.DragOnly)
        self.inputsTree.setDropIndicatorShown(True)

        def _mimeDataAlgorithm(items):
            item = items[0]
            mimeData = None
            if isinstance(item, TreeAlgorithmItem):
                mimeData = QMimeData()
                mimeData.setText(item.alg.id())
            return mimeData

        self.algorithmTree.mimeData = _mimeDataAlgorithm

        self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly)
        self.algorithmTree.setDropIndicatorShown(True)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(
                QCoreApplication.translate('ModelerDialog', 'Search…'))
        if hasattr(self.textName, 'setPlaceholderText'):
            self.textName.setPlaceholderText(self.tr('Enter model name here'))
        if hasattr(self.textGroup, 'setPlaceholderText'):
            self.textGroup.setPlaceholderText(self.tr('Enter group name here'))

        # Connect signals and slots
        self.inputsTree.doubleClicked.connect(self.addInput)
        self.searchBox.textChanged.connect(self.textChanged)
        self.algorithmTree.doubleClicked.connect(self.addAlgorithm)

        # Ctrl+= should also trigger a zoom in action
        ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self)
        ctrlEquals.activated.connect(self.zoomIn)

        self.mActionOpen.triggered.connect(self.openModel)
        self.mActionSave.triggered.connect(self.save)
        self.mActionSaveAs.triggered.connect(self.saveAs)
        self.mActionZoomIn.triggered.connect(self.zoomIn)
        self.mActionZoomOut.triggered.connect(self.zoomOut)
        self.mActionZoomActual.triggered.connect(self.zoomActual)
        self.mActionZoomToItems.triggered.connect(self.zoomToItems)
        self.mActionExportImage.triggered.connect(self.exportAsImage)
        self.mActionExportPdf.triggered.connect(self.exportAsPdf)
        self.mActionExportSvg.triggered.connect(self.exportAsSvg)
        #self.mActionExportPython.triggered.connect(self.exportAsPython)
        self.mActionEditHelp.triggered.connect(self.editHelp)
        self.mActionRun.triggered.connect(self.runModel)

        if model is not None:
            self.model = model.create()
            self.model.setSourceFilePath(model.sourceFilePath())
            self.textGroup.setText(self.model.group())
            self.textName.setText(self.model.displayName())
            self.repaintModel()

        else:
            self.model = QgsProcessingModelAlgorithm()
            self.model.setProvider(
                QgsApplication.processingRegistry().providerById('model'))

        self.fillInputsTree()
        self.fillTreeUsingProviders()

        self.view.centerOn(0, 0)
        self.help = None

        self.hasChanged = False

    def closeEvent(self, evt):
        settings = QgsSettings()
        settings.setValue("/Processing/stateModeler", self.saveState())
        settings.setValue("/Processing/geometryModeler", self.saveGeometry())

        if self.hasChanged:
            ret = QMessageBox.question(
                self, self.tr('Save Model?'),
                self.
                tr('There are unsaved changes in this model. Do you want to keep those?'
                   ),
                QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard,
                QMessageBox.Cancel)

            if ret == QMessageBox.Save:
                self.saveModel(False)
                evt.accept()
            elif ret == QMessageBox.Discard:
                evt.accept()
            else:
                evt.ignore()
        else:
            evt.accept()

    def editHelp(self):
        alg = self.model
        dlg = HelpEditionDialog(alg)
        dlg.exec_()
        if dlg.descriptions:
            self.model.setHelpContent(dlg.descriptions)
            self.hasChanged = True

    def runModel(self):
        if len(self.model.childAlgorithms()) == 0:
            self.bar.pushMessage(
                "",
                self.
                tr("Model doesn't contain any algorithm and/or parameter and can't be executed"
                   ),
                level=Qgis.Warning,
                duration=5)
            return

        dlg = AlgorithmDialog(self.model)
        dlg.exec_()

    def save(self):
        self.saveModel(False)

    def saveAs(self):
        self.saveModel(True)

    def zoomIn(self):
        self.view.setTransformationAnchor(QGraphicsView.NoAnchor)
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))

        settings = QgsSettings()
        factor = settings.value('/qgis/zoom_favor', 2.0)

        self.view.scale(factor, factor)
        self.view.centerOn(point)
        self.repaintModel()

    def zoomOut(self):
        self.view.setTransformationAnchor(QGraphicsView.NoAnchor)
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))

        settings = QgsSettings()
        factor = settings.value('/qgis/zoom_favor', 2.0)
        factor = 1 / factor

        self.view.scale(factor, factor)
        self.view.centerOn(point)
        self.repaintModel()

    def zoomActual(self):
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))
        self.view.resetTransform()
        self.view.centerOn(point)

    def zoomToItems(self):
        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        self.view.fitInView(totalRect, Qt.KeepAspectRatio)

    def exportAsImage(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As Image'), '',
            self.tr('PNG files (*.png *.PNG)'))
        if not filename:
            return

        if not filename.lower().endswith('.png'):
            filename += '.png'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        imgRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        img = QImage(totalRect.width(), totalRect.height(),
                     QImage.Format_ARGB32_Premultiplied)
        img.fill(Qt.white)
        painter = QPainter()
        painter.setRenderHint(QPainter.Antialiasing)
        painter.begin(img)
        self.scene.render(painter, imgRect, totalRect)
        painter.end()

        img.save(filename)

        self.bar.pushMessage("",
                             self.tr("Model was correctly exported as image"),
                             level=Qgis.Success,
                             duration=5)
        self.repaintModel(controls=True)

    def exportAsPdf(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As PDF'), '',
            self.tr('PDF files (*.pdf *.PDF)'))
        if not filename:
            return

        if not filename.lower().endswith('.pdf'):
            filename += '.pdf'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        printerRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        printer = QPrinter()
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(filename)
        printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()),
                             QPrinter.DevicePixel)
        printer.setFullPage(True)

        painter = QPainter(printer)
        self.scene.render(painter, printerRect, totalRect)
        painter.end()

        self.bar.pushMessage("",
                             self.tr("Model was correctly exported as PDF"),
                             level=Qgis.Success,
                             duration=5)
        self.repaintModel(controls=True)

    def exportAsSvg(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As SVG'), '',
            self.tr('SVG files (*.svg *.SVG)'))
        if not filename:
            return

        if not filename.lower().endswith('.svg'):
            filename += '.svg'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        svgRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        svg = QSvgGenerator()
        svg.setFileName(filename)
        svg.setSize(QSize(totalRect.width(), totalRect.height()))
        svg.setViewBox(svgRect)
        svg.setTitle(self.model.displayName())

        painter = QPainter(svg)
        self.scene.render(painter, svgRect, totalRect)
        painter.end()

        self.bar.pushMessage("",
                             self.tr("Model was correctly exported as SVG"),
                             level=Qgis.Success,
                             duration=5)
        self.repaintModel(controls=True)

    def exportAsPython(self):
        filename, filter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As Python Script'), '',
            self.tr('Python files (*.py *.PY)'))
        if not filename:
            return

        if not filename.lower().endswith('.py'):
            filename += '.py'

        text = self.model.asPythonCode()
        with codecs.open(filename, 'w', encoding='utf-8') as fout:
            fout.write(text)

        self.bar.pushMessage(
            "",
            self.tr("Model was correctly exported as python script"),
            level=Qgis.Success,
            duration=5)

    def saveModel(self, saveAs):
        if str(self.textGroup.text()).strip() == '' \
                or str(self.textName.text()).strip() == '':
            QMessageBox.warning(
                self, self.tr('Warning'),
                self.tr('Please enter group and model names before saving'))
            return
        self.model.setName(str(self.textName.text()))
        self.model.setGroup(str(self.textGroup.text()))
        if self.model.sourceFilePath() and not saveAs:
            filename = self.model.sourceFilePath()
        else:
            filename, filter = QFileDialog.getSaveFileName(
                self, self.tr('Save Model'),
                ModelerUtils.modelsFolders()[0],
                self.tr('Processing models (*.model3)'))
            if filename:
                if not filename.endswith('.model3'):
                    filename += '.model3'
                self.model.setSourceFilePath(filename)
        if filename:
            if not self.model.toFile(filename):
                if saveAs:
                    QMessageBox.warning(
                        self, self.tr('I/O error'),
                        self.tr('Unable to save edits. Reason:\n {0}').format(
                            str(sys.exc_info()[1])))
                else:
                    QMessageBox.warning(
                        self, self.tr("Can't save model"),
                        QCoreApplication.
                        translate('QgsPluginInstallerInstallingDialog', (
                            "This model can't be saved in its original location (probably you do not "
                            "have permission to do it). Please, use the 'Save as…' option."
                        )))
                return
            self.update_model.emit()
            self.bar.pushMessage("",
                                 self.tr("Model was correctly saved"),
                                 level=Qgis.Success,
                                 duration=5)

            self.hasChanged = False

    def openModel(self):
        filename, selected_filter = QFileDialog.getOpenFileName(
            self, self.tr('Open Model'),
            ModelerUtils.modelsFolders()[0],
            self.tr('Processing models (*.model3 *.MODEL3)'))
        if filename:
            self.loadModel(filename)

    def loadModel(self, filename):
        alg = QgsProcessingModelAlgorithm()
        if alg.fromFile(filename):
            self.model = alg
            self.model.setProvider(
                QgsApplication.processingRegistry().providerById('model'))
            self.textGroup.setText(alg.group())
            self.textName.setText(alg.name())
            self.repaintModel()

            self.view.centerOn(0, 0)
            self.hasChanged = False
        else:
            QgsMessageLog.logMessage(
                self.tr('Could not load model {0}').format(filename),
                self.tr('Processing'), Qgis.Critical)
            QMessageBox.critical(
                self, self.tr('Open Model'),
                self.tr('The selected model could not be loaded.\n'
                        'See the log for more information.'))

    def repaintModel(self, controls=True):
        self.scene = ModelerScene(self, dialog=self)
        self.scene.setSceneRect(
            QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
        self.scene.paintModel(self.model, controls)
        self.view.setScene(self.scene)

    def addInput(self):
        item = self.inputsTree.currentItem()
        param = item.data(0, Qt.UserRole)
        self.addInputOfType(param)

    def addInputOfType(self, paramType, pos=None):
        dlg = ModelerParameterDefinitionDialog(self.model, paramType)
        dlg.exec_()
        if dlg.param is not None:
            if pos is None:
                pos = self.getPositionForParameterItem()
            if isinstance(pos, QPoint):
                pos = QPointF(pos)
            component = QgsProcessingModelParameter(dlg.param.name())
            component.setDescription(dlg.param.name())
            component.setPosition(pos)
            self.model.addModelParameter(dlg.param, component)
            self.repaintModel()
            # self.view.ensureVisible(self.scene.getLastParameterItem())
            self.hasChanged = True

    def getPositionForParameterItem(self):
        MARGIN = 20
        BOX_WIDTH = 200
        BOX_HEIGHT = 80
        if len(self.model.parameterComponents()) > 0:
            maxX = max([
                i.position().x()
                for i in list(self.model.parameterComponents().values())
            ])
            newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH)
        else:
            newX = MARGIN + BOX_WIDTH / 2
        return QPointF(newX, MARGIN + BOX_HEIGHT / 2)

    def textChanged(self):
        text = self.searchBox.text().strip(' ').lower()
        for item in list(self.disabledProviderItems.values()):
            item.setHidden(True)
        self._filterItem(self.algorithmTree.invisibleRootItem(),
                         [t for t in text.split(' ') if t])
        if text:
            self.algorithmTree.expandAll()
            self.disabledWithMatchingAlgs = []
            for provider in QgsApplication.processingRegistry().providers():
                if not provider.isActive():
                    for alg in provider.algorithms():
                        if text in alg.name():
                            self.disabledWithMatchingAlgs.append(provider.id())
                            break
        else:
            self.algorithmTree.collapseAll()

    def _filterItem(self, item, text):
        if (item.childCount() > 0):
            show = False
            for i in range(item.childCount()):
                child = item.child(i)
                showChild = self._filterItem(child, text)
                show = (showChild or show) and item not in list(
                    self.disabledProviderItems.values())
            item.setHidden(not show)
            return show
        elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)):
            # hide if every part of text is not contained somewhere in either the item text or item user role
            item_text = [
                item.text(0).lower(),
                item.data(0, ModelerDialog.NAME_ROLE).lower()
            ]
            if isinstance(item, TreeAlgorithmItem):
                item_text.append(item.alg.id().lower())
                if item.alg.shortDescription():
                    item_text.append(item.alg.shortDescription().lower())
                item_text.extend(
                    [t.lower() for t in item.data(0, ModelerDialog.TAG_ROLE)])

            hide = bool(text) and not all(
                any(part in t for t in item_text) for part in text)

            item.setHidden(hide)
            return not hide
        else:
            item.setHidden(True)
            return False

    def fillInputsTree(self):
        icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg'))
        parametersItem = QTreeWidgetItem()
        parametersItem.setText(0, self.tr('Parameters'))
        sortedParams = sorted(
            QgsApplication.instance().processingRegistry().parameterTypes(),
            key=lambda pt: pt.name())
        for param in sortedParams:
            if param.flags() & QgsProcessingParameterType.ExposeToModeler:
                paramItem = QTreeWidgetItem()
                paramItem.setText(0, param.name())
                paramItem.setData(0, Qt.UserRole, param.id())
                paramItem.setIcon(0, icon)
                paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable
                                   | Qt.ItemIsDragEnabled)
                paramItem.setToolTip(0, param.description())
                parametersItem.addChild(paramItem)
        self.inputsTree.addTopLevelItem(parametersItem)
        parametersItem.setExpanded(True)

    def addAlgorithm(self):
        item = self.algorithmTree.currentItem()
        if isinstance(item, TreeAlgorithmItem):
            alg = QgsApplication.processingRegistry().createAlgorithmById(
                item.alg.id())
            self._addAlgorithm(alg)

    def _addAlgorithm(self, alg, pos=None):
        dlg = ModelerParametersDialog(alg, self.model)
        if dlg.exec_():
            alg = dlg.createAlgorithm()
            if pos is None:
                alg.setPosition(self.getPositionForAlgorithmItem())
            else:
                alg.setPosition(pos)
            from processing.modeler.ModelerGraphicItem import ModelerGraphicItem
            for i, out in enumerate(alg.modelOutputs()):
                alg.modelOutput(out).setPosition(
                    alg.position() +
                    QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) *
                            ModelerGraphicItem.BOX_HEIGHT))
            self.model.addChildAlgorithm(alg)
            self.repaintModel()
            self.hasChanged = True

    def getPositionForAlgorithmItem(self):
        MARGIN = 20
        BOX_WIDTH = 200
        BOX_HEIGHT = 80
        if self.model.childAlgorithms():
            maxX = max([
                alg.position().x()
                for alg in list(self.model.childAlgorithms().values())
            ])
            maxY = max([
                alg.position().y()
                for alg in list(self.model.childAlgorithms().values())
            ])
            newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH)
            newY = min(MARGIN + BOX_HEIGHT + maxY,
                       self.CANVAS_SIZE - BOX_HEIGHT)
        else:
            newX = MARGIN + BOX_WIDTH / 2
            newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2
        return QPointF(newX, newY)

    def fillTreeUsingProviders(self):
        self.algorithmTree.clear()
        self.disabledProviderItems = {}

        # TODO - replace with proper model for toolbox!

        # first add qgis/native providers, since they create top level groups
        for provider in QgsApplication.processingRegistry().providers():
            if provider.id() in ('qgis', 'native', '3d'):
                self.addAlgorithmsFromProvider(
                    provider, self.algorithmTree.invisibleRootItem())
            else:
                continue
        self.algorithmTree.sortItems(0, Qt.AscendingOrder)

        for provider in QgsApplication.processingRegistry().providers():
            if provider.id() in ('qgis', 'native', '3d'):
                # already added
                continue
            else:
                providerItem = TreeProviderItem(provider, self.algorithmTree,
                                                self)

                # insert non-native providers at end of tree, alphabetically
                for i in range(
                        self.algorithmTree.invisibleRootItem().childCount()):
                    child = self.algorithmTree.invisibleRootItem().child(i)
                    if isinstance(child, TreeProviderItem):
                        if child.text(0) > providerItem.text(0):
                            break

                self.algorithmTree.insertTopLevelItem(i + 1, providerItem)

                if not provider.isActive():
                    providerItem.setHidden(True)
                    self.disabledProviderItems[provider.id()] = providerItem

    def addAlgorithmsFromProvider(self, provider, parent):
        groups = {}
        count = 0
        algs = provider.algorithms()
        active = provider.isActive()

        # Add algorithms
        for alg in algs:
            if alg.flags() & QgsProcessingAlgorithm.FlagHideFromModeler:
                continue
            groupItem = None
            if alg.group() in groups:
                groupItem = groups[alg.group()]
            else:
                # check if group already exists
                for i in range(parent.childCount()):
                    if parent.child(i).text(0) == alg.group():
                        groupItem = parent.child(i)
                        groups[alg.group()] = groupItem
                        break

                if not groupItem:
                    groupItem = TreeGroupItem(alg.group())
                    if not active:
                        groupItem.setInactive()
                    if provider.id() in ('qgis', 'native', '3d'):
                        groupItem.setIcon(0, provider.icon())
                    groups[alg.group()] = groupItem
            algItem = TreeAlgorithmItem(alg)
            if not active:
                algItem.setForeground(0, Qt.darkGray)
            groupItem.addChild(algItem)
            count += 1

        text = provider.name()

        if not provider.id() in ('qgis', 'native', '3d'):
            if not active:

                def activateProvider():
                    self.activateProvider(provider.id())

                label = QLabel(
                    text + "&nbsp;&nbsp;&nbsp;&nbsp;<a href='%s'>Activate</a>")
                label.setStyleSheet(
                    "QLabel {background-color: white; color: grey;}")
                label.linkActivated.connect(activateProvider)
                self.algorithmTree.setItemWidget(parent, 0, label)
            else:
                parent.setText(0, text)

        for group, groupItem in sorted(groups.items(),
                                       key=operator.itemgetter(1)):
            parent.addChild(groupItem)

        if not provider.id() in ('qgis', 'native', '3d'):
            parent.setHidden(parent.childCount() == 0)
コード例 #14
0
class SettingsWatcher(QObject):
    settingsChanged = pyqtSignal()
コード例 #15
0
ファイル: ModelerDialog.py プロジェクト: Visnu7777/QGIS
class ModelerDialog(BASE, WIDGET):
    ALG_ITEM = 'ALG_ITEM'
    PROVIDER_ITEM = 'PROVIDER_ITEM'
    GROUP_ITEM = 'GROUP_ITEM'

    NAME_ROLE = Qt.UserRole
    TAG_ROLE = Qt.UserRole + 1
    TYPE_ROLE = Qt.UserRole + 2

    CANVAS_SIZE = 4000

    update_model = pyqtSignal()

    def __init__(self, model=None):
        super().__init__(None)
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.setupUi(self)

        self._variables_scope = None

        # LOTS of bug reports when we include the dock creation in the UI file
        # see e.g. #16428, #19068
        # So just roll it all by hand......!
        self.propertiesDock = QgsDockWidget(self)
        self.propertiesDock.setFeatures(QDockWidget.DockWidgetFloatable
                                        | QDockWidget.DockWidgetMovable)
        self.propertiesDock.setObjectName("propertiesDock")
        propertiesDockContents = QWidget()
        self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents)
        self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0)
        self.verticalDockLayout_1.setSpacing(0)
        self.scrollArea_1 = QgsScrollArea(propertiesDockContents)
        sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding,
                                 QSizePolicy.MinimumExpanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.scrollArea_1.sizePolicy().hasHeightForWidth())
        self.scrollArea_1.setSizePolicy(sizePolicy)
        self.scrollArea_1.setFocusPolicy(Qt.WheelFocus)
        self.scrollArea_1.setFrameShape(QFrame.NoFrame)
        self.scrollArea_1.setFrameShadow(QFrame.Plain)
        self.scrollArea_1.setWidgetResizable(True)
        self.scrollAreaWidgetContents_1 = QWidget()
        self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1)
        self.gridLayout.setContentsMargins(6, 6, 6, 6)
        self.gridLayout.setSpacing(4)
        self.label_1 = QLabel(self.scrollAreaWidgetContents_1)
        self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1)
        self.textName = QLineEdit(self.scrollAreaWidgetContents_1)
        self.gridLayout.addWidget(self.textName, 0, 1, 1, 1)
        self.label_2 = QLabel(self.scrollAreaWidgetContents_1)
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1)
        self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1)
        self.label_1.setText(self.tr("Name"))
        self.textName.setToolTip(self.tr("Enter model name here"))
        self.label_2.setText(self.tr("Group"))
        self.textGroup.setToolTip(self.tr("Enter group name here"))
        self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1)
        self.verticalDockLayout_1.addWidget(self.scrollArea_1)
        self.propertiesDock.setWidget(propertiesDockContents)
        self.propertiesDock.setWindowTitle(self.tr("Model Properties"))

        self.inputsDock = QgsDockWidget(self)
        self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable
                                    | QDockWidget.DockWidgetMovable)
        self.inputsDock.setObjectName("inputsDock")
        self.inputsDockContents = QWidget()
        self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents)
        self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
        self.scrollArea_2 = QgsScrollArea(self.inputsDockContents)
        sizePolicy.setHeightForWidth(
            self.scrollArea_2.sizePolicy().hasHeightForWidth())
        self.scrollArea_2.setSizePolicy(sizePolicy)
        self.scrollArea_2.setFocusPolicy(Qt.WheelFocus)
        self.scrollArea_2.setFrameShape(QFrame.NoFrame)
        self.scrollArea_2.setFrameShadow(QFrame.Plain)
        self.scrollArea_2.setWidgetResizable(True)
        self.scrollAreaWidgetContents_2 = QWidget()
        self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setSpacing(0)
        self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2)
        self.inputsTree.setAlternatingRowColors(True)
        self.inputsTree.header().setVisible(False)
        self.verticalLayout.addWidget(self.inputsTree)
        self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
        self.verticalLayout_3.addWidget(self.scrollArea_2)
        self.inputsDock.setWidget(self.inputsDockContents)
        self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock)
        self.inputsDock.setWindowTitle(self.tr("Inputs"))

        self.algorithmsDock = QgsDockWidget(self)
        self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable
                                        | QDockWidget.DockWidgetMovable)
        self.algorithmsDock.setObjectName("algorithmsDock")
        self.algorithmsDockContents = QWidget()
        self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents)
        self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
        self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents)
        sizePolicy.setHeightForWidth(
            self.scrollArea_3.sizePolicy().hasHeightForWidth())
        self.scrollArea_3.setSizePolicy(sizePolicy)
        self.scrollArea_3.setFocusPolicy(Qt.WheelFocus)
        self.scrollArea_3.setFrameShape(QFrame.NoFrame)
        self.scrollArea_3.setFrameShadow(QFrame.Plain)
        self.scrollArea_3.setWidgetResizable(True)
        self.scrollAreaWidgetContents_3 = QWidget()
        self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3)
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_2.setSpacing(4)
        self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3)
        self.verticalLayout_2.addWidget(self.searchBox)
        self.algorithmTree = QgsProcessingToolboxTreeView(
            None, QgsApplication.processingRegistry())
        self.algorithmTree.setAlternatingRowColors(True)
        self.algorithmTree.header().setVisible(False)
        self.verticalLayout_2.addWidget(self.algorithmTree)
        self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3)
        self.verticalLayout_4.addWidget(self.scrollArea_3)
        self.algorithmsDock.setWidget(self.algorithmsDockContents)
        self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock)
        self.algorithmsDock.setWindowTitle(self.tr("Algorithms"))
        self.searchBox.setToolTip(
            self.tr("Enter algorithm name to filter list"))
        self.searchBox.setShowSearchIcon(True)

        self.variables_dock = QgsDockWidget(self)
        self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable
                                        | QDockWidget.DockWidgetMovable)
        self.variables_dock.setObjectName("variablesDock")
        self.variables_dock_contents = QWidget()
        vl_v = QVBoxLayout()
        vl_v.setContentsMargins(0, 0, 0, 0)
        self.variables_editor = QgsVariableEditorWidget()
        vl_v.addWidget(self.variables_editor)
        self.variables_dock_contents.setLayout(vl_v)
        self.variables_dock.setWidget(self.variables_dock_contents)
        self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock)
        self.variables_dock.setWindowTitle(self.tr("Variables"))
        self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock)
        self.tabifyDockWidget(self.propertiesDock, self.variables_dock)
        self.variables_editor.scopeChanged.connect(self.variables_changed)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.centralWidget().layout().insertWidget(0, self.bar)

        try:
            self.setDockOptions(self.dockOptions()
                                | QMainWindow.GroupedDragging)
        except:
            pass

        if iface is not None:
            self.mToolbar.setIconSize(iface.iconSize())
            self.setStyleSheet(iface.mainWindow().styleSheet())

        self.toolbutton_export_to_script = QToolButton()
        self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup)
        self.export_to_script_algorithm_action = QAction(
            QCoreApplication.translate('ModelerDialog',
                                       'Export as Script Algorithm…'))
        self.toolbutton_export_to_script.addActions(
            [self.export_to_script_algorithm_action])
        self.mToolbar.insertWidget(self.mActionExportImage,
                                   self.toolbutton_export_to_script)
        self.export_to_script_algorithm_action.triggered.connect(
            self.export_as_script_algorithm)

        self.mActionOpen.setIcon(
            QgsApplication.getThemeIcon('/mActionFileOpen.svg'))
        self.mActionSave.setIcon(
            QgsApplication.getThemeIcon('/mActionFileSave.svg'))
        self.mActionSaveAs.setIcon(
            QgsApplication.getThemeIcon('/mActionFileSaveAs.svg'))
        self.mActionSaveInProject.setIcon(
            QgsApplication.getThemeIcon('/mAddToProject.svg'))
        self.mActionZoomActual.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomActual.svg'))
        self.mActionZoomIn.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomIn.svg'))
        self.mActionZoomOut.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomOut.svg'))
        self.mActionExportImage.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg'))
        self.mActionZoomToItems.setIcon(
            QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg'))
        self.mActionExportPdf.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg'))
        self.mActionExportSvg.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg'))
        self.toolbutton_export_to_script.setIcon(
            QgsApplication.getThemeIcon('/mActionSaveAsPython.svg'))
        self.mActionEditHelp.setIcon(
            QgsApplication.getThemeIcon('/mActionEditHelpContent.svg'))
        self.mActionRun.setIcon(
            QgsApplication.getThemeIcon('/mActionStart.svg'))

        self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock)
        self.tabifyDockWidget(self.inputsDock, self.algorithmsDock)
        self.inputsDock.raise_()

        self.setWindowFlags(Qt.WindowMinimizeButtonHint
                            | Qt.WindowMaximizeButtonHint
                            | Qt.WindowCloseButtonHint)

        settings = QgsSettings()
        self.restoreState(
            settings.value("/Processing/stateModeler", QByteArray()))
        self.restoreGeometry(
            settings.value("/Processing/geometryModeler", QByteArray()))

        self.scene = ModelerScene(self, dialog=self)
        self.scene.setSceneRect(
            QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))

        self.view.setScene(self.scene)
        self.view.setAcceptDrops(True)
        self.view.ensureVisible(0, 0, 10, 10)
        self.view.scale(QgsApplication.desktop().logicalDpiX() / 96,
                        QgsApplication.desktop().logicalDpiX() / 96)

        def _dragEnterEvent(event):
            if event.mimeData().hasText() or event.mimeData().hasFormat(
                    'application/x-vnd.qgis.qgis.algorithmid'):
                event.acceptProposedAction()
            else:
                event.ignore()

        def _dropEvent(event):
            def alg_dropped(algorithm_id, pos):
                alg = QgsApplication.processingRegistry().createAlgorithmById(
                    algorithm_id)
                if alg is not None:
                    self._addAlgorithm(alg, pos)
                else:
                    assert False, algorithm_id

            def input_dropped(id, pos):
                if id in [
                        param.id() for param in QgsApplication.instance().
                        processingRegistry().parameterTypes()
                ]:
                    self.addInputOfType(itemId, pos)

            if event.mimeData().hasFormat(
                    'application/x-vnd.qgis.qgis.algorithmid'):
                data = event.mimeData().data(
                    'application/x-vnd.qgis.qgis.algorithmid')
                stream = QDataStream(data, QIODevice.ReadOnly)
                algorithm_id = stream.readQString()
                QTimer.singleShot(
                    0,
                    lambda id=algorithm_id, pos=self.view.mapToScene(event.pos(
                    )): alg_dropped(id, pos))
                event.accept()
            elif event.mimeData().hasText():
                itemId = event.mimeData().text()
                QTimer.singleShot(0,
                                  lambda id=itemId, pos=self.view.mapToScene(
                                      event.pos()): input_dropped(id, pos))
                event.accept()
            else:
                event.ignore()

        def _dragMoveEvent(event):
            if event.mimeData().hasText() or event.mimeData().hasFormat(
                    'application/x-vnd.qgis.qgis.algorithmid'):
                event.accept()
            else:
                event.ignore()

        def _wheelEvent(event):
            self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)

            settings = QgsSettings()
            factor = settings.value('/qgis/zoom_favor', 2.0)

            # "Normal" mouse has an angle delta of 120, precision mouses provide data
            # faster, in smaller steps
            factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y())

            if (event.modifiers() == Qt.ControlModifier):
                factor = 1.0 + (factor - 1.0) / 20.0

            if event.angleDelta().y() < 0:
                factor = 1 / factor

            self.view.scale(factor, factor)

        def _enterEvent(e):
            QGraphicsView.enterEvent(self.view, e)
            self.view.viewport().setCursor(Qt.ArrowCursor)

        def _mouseReleaseEvent(e):
            QGraphicsView.mouseReleaseEvent(self.view, e)
            self.view.viewport().setCursor(Qt.ArrowCursor)

        def _mousePressEvent(e):
            if e.button() == Qt.MidButton:
                self.previousMousePos = e.pos()
            else:
                QGraphicsView.mousePressEvent(self.view, e)

        def _mouseMoveEvent(e):
            if e.buttons() == Qt.MidButton:
                offset = self.previousMousePos - e.pos()
                self.previousMousePos = e.pos()

                self.view.verticalScrollBar().setValue(
                    self.view.verticalScrollBar().value() + offset.y())
                self.view.horizontalScrollBar().setValue(
                    self.view.horizontalScrollBar().value() + offset.x())
            else:
                QGraphicsView.mouseMoveEvent(self.view, e)

        self.view.setDragMode(QGraphicsView.ScrollHandDrag)
        self.view.dragEnterEvent = _dragEnterEvent
        self.view.dropEvent = _dropEvent
        self.view.dragMoveEvent = _dragMoveEvent
        self.view.wheelEvent = _wheelEvent
        self.view.enterEvent = _enterEvent
        self.view.mousePressEvent = _mousePressEvent
        self.view.mouseMoveEvent = _mouseMoveEvent

        def _mimeDataInput(items):
            mimeData = QMimeData()
            text = items[0].data(0, Qt.UserRole)
            mimeData.setText(text)
            return mimeData

        self.inputsTree.mimeData = _mimeDataInput

        self.inputsTree.setDragDropMode(QTreeWidget.DragOnly)
        self.inputsTree.setDropIndicatorShown(True)

        self.algorithms_model = ModelerToolboxModel(
            self, QgsApplication.processingRegistry())
        self.algorithmTree.setToolboxProxyModel(self.algorithms_model)
        self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly)
        self.algorithmTree.setDropIndicatorShown(True)

        filters = QgsProcessingToolboxProxyModel.Filters(
            QgsProcessingToolboxProxyModel.FilterModeler)
        if ProcessingConfig.getSetting(
                ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES):
            filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues
        self.algorithmTree.setFilters(filters)

        if hasattr(self.searchBox, 'setPlaceholderText'):
            self.searchBox.setPlaceholderText(
                QCoreApplication.translate('ModelerDialog', 'Search…'))
        if hasattr(self.textName, 'setPlaceholderText'):
            self.textName.setPlaceholderText(self.tr('Enter model name here'))
        if hasattr(self.textGroup, 'setPlaceholderText'):
            self.textGroup.setPlaceholderText(self.tr('Enter group name here'))

        # Connect signals and slots
        self.inputsTree.doubleClicked.connect(self.addInput)
        self.searchBox.textChanged.connect(self.algorithmTree.setFilterString)
        self.algorithmTree.doubleClicked.connect(self.addAlgorithm)

        # Ctrl+= should also trigger a zoom in action
        ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self)
        ctrlEquals.activated.connect(self.zoomIn)

        self.mActionOpen.triggered.connect(self.openModel)
        self.mActionSave.triggered.connect(self.save)
        self.mActionSaveAs.triggered.connect(self.saveAs)
        self.mActionSaveInProject.triggered.connect(self.saveInProject)
        self.mActionZoomIn.triggered.connect(self.zoomIn)
        self.mActionZoomOut.triggered.connect(self.zoomOut)
        self.mActionZoomActual.triggered.connect(self.zoomActual)
        self.mActionZoomToItems.triggered.connect(self.zoomToItems)
        self.mActionExportImage.triggered.connect(self.exportAsImage)
        self.mActionExportPdf.triggered.connect(self.exportAsPdf)
        self.mActionExportSvg.triggered.connect(self.exportAsSvg)
        #self.mActionExportPython.triggered.connect(self.exportAsPython)
        self.mActionEditHelp.triggered.connect(self.editHelp)
        self.mActionRun.triggered.connect(self.runModel)

        if model is not None:
            self.model = model.create()
            self.model.setSourceFilePath(model.sourceFilePath())
            self.textGroup.setText(self.model.group())
            self.textName.setText(self.model.displayName())
            self.repaintModel()

        else:
            self.model = QgsProcessingModelAlgorithm()
            self.model.setProvider(
                QgsApplication.processingRegistry().providerById('model'))
        self.update_variables_gui()

        self.fillInputsTree()

        self.view.centerOn(0, 0)
        self.help = None

        self.hasChanged = False

    def closeEvent(self, evt):
        settings = QgsSettings()
        settings.setValue("/Processing/stateModeler", self.saveState())
        settings.setValue("/Processing/geometryModeler", self.saveGeometry())

        if self.hasChanged:
            ret = QMessageBox.question(
                self, self.tr('Save Model?'),
                self.
                tr('There are unsaved changes in this model. Do you want to keep those?'
                   ),
                QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard,
                QMessageBox.Cancel)

            if ret == QMessageBox.Save:
                self.saveModel(False)
                evt.accept()
            elif ret == QMessageBox.Discard:
                evt.accept()
            else:
                evt.ignore()
        else:
            evt.accept()

    def editHelp(self):
        alg = self.model
        dlg = HelpEditionDialog(alg)
        dlg.exec_()
        if dlg.descriptions:
            self.model.setHelpContent(dlg.descriptions)
            self.hasChanged = True

    def update_variables_gui(self):
        variables_scope = QgsExpressionContextScope(self.tr('Model Variables'))
        for k, v in self.model.variables().items():
            variables_scope.setVariable(k, v)
        variables_context = QgsExpressionContext()
        variables_context.appendScope(variables_scope)
        self.variables_editor.setContext(variables_context)
        self.variables_editor.setEditableScopeIndex(0)

    def variables_changed(self):
        self.model.setVariables(self.variables_editor.variablesInActiveScope())

    def runModel(self):
        if len(self.model.childAlgorithms()) == 0:
            self.bar.pushMessage(
                "",
                self.
                tr("Model doesn't contain any algorithm and/or parameter and can't be executed"
                   ),
                level=Qgis.Warning,
                duration=5)
            return

        dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow())
        dlg.exec_()

    def save(self):
        self.saveModel(False)

    def saveAs(self):
        self.saveModel(True)

    def saveInProject(self):
        if not self.can_save():
            return

        self.model.setName(str(self.textName.text()))
        self.model.setGroup(str(self.textGroup.text()))
        self.model.setSourceFilePath(None)

        project_provider = QgsApplication.processingRegistry().providerById(
            PROJECT_PROVIDER_ID)
        project_provider.add_model(self.model)

        self.update_model.emit()
        self.bar.pushMessage("",
                             self.tr("Model was saved inside current project"),
                             level=Qgis.Success,
                             duration=5)

        self.hasChanged = False
        QgsProject.instance().setDirty(True)

    def zoomIn(self):
        self.view.setTransformationAnchor(QGraphicsView.NoAnchor)
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))

        settings = QgsSettings()
        factor = settings.value('/qgis/zoom_favor', 2.0)

        self.view.scale(factor, factor)
        self.view.centerOn(point)
        self.repaintModel()

    def zoomOut(self):
        self.view.setTransformationAnchor(QGraphicsView.NoAnchor)
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))

        settings = QgsSettings()
        factor = settings.value('/qgis/zoom_favor', 2.0)
        factor = 1 / factor

        self.view.scale(factor, factor)
        self.view.centerOn(point)
        self.repaintModel()

    def zoomActual(self):
        point = self.view.mapToScene(
            QPoint(self.view.viewport().width() / 2,
                   self.view.viewport().height() / 2))
        self.view.resetTransform()
        self.view.scale(QgsApplication.desktop().logicalDpiX() / 96,
                        QgsApplication.desktop().logicalDpiX() / 96)
        self.view.centerOn(point)

    def zoomToItems(self):
        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        self.view.fitInView(totalRect, Qt.KeepAspectRatio)

    def exportAsImage(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As Image'), '',
            self.tr('PNG files (*.png *.PNG)'))
        if not filename:
            return

        if not filename.lower().endswith('.png'):
            filename += '.png'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        imgRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        img = QImage(totalRect.width(), totalRect.height(),
                     QImage.Format_ARGB32_Premultiplied)
        img.fill(Qt.white)
        painter = QPainter()
        painter.setRenderHint(QPainter.Antialiasing)
        painter.begin(img)
        self.scene.render(painter, imgRect, totalRect)
        painter.end()

        img.save(filename)

        self.bar.pushMessage(
            "",
            self.tr(
                "Successfully exported model as image to <a href=\"{}\">{}</a>"
            ).format(
                QUrl.fromLocalFile(filename).toString(),
                QDir.toNativeSeparators(filename)),
            level=Qgis.Success,
            duration=5)
        self.repaintModel(controls=True)

    def exportAsPdf(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As PDF'), '',
            self.tr('PDF files (*.pdf *.PDF)'))
        if not filename:
            return

        if not filename.lower().endswith('.pdf'):
            filename += '.pdf'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        printerRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        printer = QPrinter()
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName(filename)
        printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()),
                             QPrinter.DevicePixel)
        printer.setFullPage(True)

        painter = QPainter(printer)
        self.scene.render(painter, printerRect, totalRect)
        painter.end()

        self.bar.pushMessage(
            "",
            self.tr(
                "Successfully exported model as PDF to <a href=\"{}\">{}</a>").
            format(
                QUrl.fromLocalFile(filename).toString(),
                QDir.toNativeSeparators(filename)),
            level=Qgis.Success,
            duration=5)
        self.repaintModel(controls=True)

    def exportAsSvg(self):
        self.repaintModel(controls=False)
        filename, fileFilter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As SVG'), '',
            self.tr('SVG files (*.svg *.SVG)'))
        if not filename:
            return

        if not filename.lower().endswith('.svg'):
            filename += '.svg'

        totalRect = self.scene.itemsBoundingRect()
        totalRect.adjust(-10, -10, 10, 10)
        svgRect = QRectF(0, 0, totalRect.width(), totalRect.height())

        svg = QSvgGenerator()
        svg.setFileName(filename)
        svg.setSize(QSize(totalRect.width(), totalRect.height()))
        svg.setViewBox(svgRect)
        svg.setTitle(self.model.displayName())

        painter = QPainter(svg)
        self.scene.render(painter, svgRect, totalRect)
        painter.end()

        self.bar.pushMessage(
            "",
            self.tr(
                "Successfully exported model as SVG to <a href=\"{}\">{}</a>").
            format(
                QUrl.fromLocalFile(filename).toString(),
                QDir.toNativeSeparators(filename)),
            level=Qgis.Success,
            duration=5)
        self.repaintModel(controls=True)

    def exportAsPython(self):
        filename, filter = QFileDialog.getSaveFileName(
            self, self.tr('Save Model As Python Script'), '',
            self.tr('Processing scripts (*.py *.PY)'))
        if not filename:
            return

        if not filename.lower().endswith('.py'):
            filename += '.py'

        text = self.model.asPythonCode()
        with codecs.open(filename, 'w', encoding='utf-8') as fout:
            fout.write(text)

        self.bar.pushMessage(
            "",
            self.
            tr("Successfully exported model as python script to <a href=\"{}\">{}</a>"
               ).format(
                   QUrl.fromLocalFile(filename).toString(),
                   QDir.toNativeSeparators(filename)),
            level=Qgis.Success,
            duration=5)

    def can_save(self):
        """
        Tests whether a model can be saved, or if it is not yet valid
        :return: bool
        """
        if str(self.textName.text()).strip() == '':
            self.bar.pushWarning(
                "", self.tr('Please a enter model name before saving'))
            return False

        return True

    def saveModel(self, saveAs):
        if not self.can_save():
            return
        self.model.setName(str(self.textName.text()))
        self.model.setGroup(str(self.textGroup.text()))
        if self.model.sourceFilePath() and not saveAs:
            filename = self.model.sourceFilePath()
        else:
            filename, filter = QFileDialog.getSaveFileName(
                self, self.tr('Save Model'),
                ModelerUtils.modelsFolders()[0],
                self.tr('Processing models (*.model3 *.MODEL3)'))
            if filename:
                if not filename.endswith('.model3'):
                    filename += '.model3'
                self.model.setSourceFilePath(filename)
        if filename:
            if not self.model.toFile(filename):
                if saveAs:
                    QMessageBox.warning(
                        self, self.tr('I/O error'),
                        self.tr('Unable to save edits. Reason:\n {0}').format(
                            str(sys.exc_info()[1])))
                else:
                    QMessageBox.warning(
                        self, self.tr("Can't save model"),
                        QCoreApplication.
                        translate('QgsPluginInstallerInstallingDialog', (
                            "This model can't be saved in its original location (probably you do not "
                            "have permission to do it). Please, use the 'Save as…' option."
                        )))
                return
            self.update_model.emit()
            if saveAs:
                self.bar.pushMessage(
                    "",
                    self.tr(
                        "Model was correctly saved to <a href=\"{}\">{}</a>").
                    format(
                        QUrl.fromLocalFile(filename).toString(),
                        QDir.toNativeSeparators(filename)),
                    level=Qgis.Success,
                    duration=5)
            else:
                self.bar.pushMessage("",
                                     self.tr("Model was correctly saved"),
                                     level=Qgis.Success,
                                     duration=5)

            self.hasChanged = False

    def openModel(self):
        filename, selected_filter = QFileDialog.getOpenFileName(
            self, self.tr('Open Model'),
            ModelerUtils.modelsFolders()[0],
            self.tr('Processing models (*.model3 *.MODEL3)'))
        if filename:
            self.loadModel(filename)

    def loadModel(self, filename):
        alg = QgsProcessingModelAlgorithm()
        if alg.fromFile(filename):
            self.model = alg
            self.model.setProvider(
                QgsApplication.processingRegistry().providerById('model'))
            self.textGroup.setText(alg.group())
            self.textName.setText(alg.name())
            self.repaintModel()

            self.update_variables_gui()

            self.view.centerOn(0, 0)
            self.hasChanged = False
        else:
            QgsMessageLog.logMessage(
                self.tr('Could not load model {0}').format(filename),
                self.tr('Processing'), Qgis.Critical)
            QMessageBox.critical(
                self, self.tr('Open Model'),
                self.tr('The selected model could not be loaded.\n'
                        'See the log for more information.'))

    def repaintModel(self, controls=True):
        self.scene = ModelerScene(self, dialog=self)
        self.scene.setSceneRect(
            QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE))
        self.scene.paintModel(self.model, controls)
        self.view.setScene(self.scene)

    def addInput(self):
        item = self.inputsTree.currentItem()
        param = item.data(0, Qt.UserRole)
        self.addInputOfType(param)

    def addInputOfType(self, paramType, pos=None):
        dlg = ModelerParameterDefinitionDialog(self.model, paramType)
        dlg.exec_()
        if dlg.param is not None:
            if pos is None:
                pos = self.getPositionForParameterItem()
            if isinstance(pos, QPoint):
                pos = QPointF(pos)
            component = QgsProcessingModelParameter(dlg.param.name())
            component.setDescription(dlg.param.name())
            component.setPosition(pos)
            self.model.addModelParameter(dlg.param, component)
            self.repaintModel()
            # self.view.ensureVisible(self.scene.getLastParameterItem())
            self.hasChanged = True

    def getPositionForParameterItem(self):
        MARGIN = 20
        BOX_WIDTH = 200
        BOX_HEIGHT = 80
        if len(self.model.parameterComponents()) > 0:
            maxX = max([
                i.position().x()
                for i in list(self.model.parameterComponents().values())
            ])
            newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH)
        else:
            newX = MARGIN + BOX_WIDTH / 2
        return QPointF(newX, MARGIN + BOX_HEIGHT / 2)

    def fillInputsTree(self):
        icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg'))
        parametersItem = QTreeWidgetItem()
        parametersItem.setText(0, self.tr('Parameters'))
        sortedParams = sorted(
            QgsApplication.instance().processingRegistry().parameterTypes(),
            key=lambda pt: pt.name())
        for param in sortedParams:
            if param.flags() & QgsProcessingParameterType.ExposeToModeler:
                paramItem = QTreeWidgetItem()
                paramItem.setText(0, param.name())
                paramItem.setData(0, Qt.UserRole, param.id())
                paramItem.setIcon(0, icon)
                paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable
                                   | Qt.ItemIsDragEnabled)
                paramItem.setToolTip(0, param.description())
                parametersItem.addChild(paramItem)
        self.inputsTree.addTopLevelItem(parametersItem)
        parametersItem.setExpanded(True)

    def addAlgorithm(self):
        algorithm = self.algorithmTree.selectedAlgorithm()
        if algorithm is not None:
            alg = QgsApplication.processingRegistry().createAlgorithmById(
                algorithm.id())
            self._addAlgorithm(alg)

    def _addAlgorithm(self, alg, pos=None):
        dlg = ModelerParametersDialog(alg, self.model)
        if dlg.exec_():
            alg = dlg.createAlgorithm()
            if pos is None:
                alg.setPosition(self.getPositionForAlgorithmItem())
            else:
                alg.setPosition(pos)
            from processing.modeler.ModelerGraphicItem import ModelerGraphicItem
            for i, out in enumerate(alg.modelOutputs()):
                alg.modelOutput(out).setPosition(
                    alg.position() +
                    QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) *
                            ModelerGraphicItem.BOX_HEIGHT))
            self.model.addChildAlgorithm(alg)
            self.repaintModel()
            self.hasChanged = True

    def getPositionForAlgorithmItem(self):
        MARGIN = 20
        BOX_WIDTH = 200
        BOX_HEIGHT = 80
        if self.model.childAlgorithms():
            maxX = max([
                alg.position().x()
                for alg in list(self.model.childAlgorithms().values())
            ])
            maxY = max([
                alg.position().y()
                for alg in list(self.model.childAlgorithms().values())
            ])
            newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH)
            newY = min(MARGIN + BOX_HEIGHT + maxY,
                       self.CANVAS_SIZE - BOX_HEIGHT)
        else:
            newX = MARGIN + BOX_WIDTH / 2
            newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2
        return QPointF(newX, newY)

    def export_as_script_algorithm(self):
        dlg = ScriptEditorDialog(None)

        dlg.editor.setText('\n'.join(
            self.model.asPythonCode(
                QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4)))
        dlg.show()
コード例 #16
0
class StatsWidget(QWidget, FORM_CLASS):

    signalAskCloseWindow = pyqtSignal(name='signalAskCloseWindow')

    def __init__(self, parent=None):
        self.parent = parent
        super(StatsWidget, self).__init__()
        self.setupUi(self)

        self.label_progressStats.setText('')

        # Connect
        # noinspection PyUnresolvedReferences
        self.pushButton_saveTable.clicked.connect(self.save_table)
        # noinspection PyUnresolvedReferences
        self.pushButton_saveYValues.clicked.connect(self.save_y_values)
        self.buttonBox_stats.button(QDialogButtonBox.Ok).clicked.connect(
            self.run_stats)
        self.buttonBox_stats.button(QDialogButtonBox.Cancel).clicked.connect(
            self.signalAskCloseWindow.emit)

        # a figure instance to plot on
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setMinimumSize(QSize(300, 0))
        self.toolbar = CustomNavigationToolbar(self.canvas, self)
        self.layout_plot.addWidget(self.toolbar)
        self.layout_plot.addWidget(self.canvas)

        self.tab = []

        self.comboBox_blurredLayer.setFilters(
            QgsMapLayerProxyModel.PolygonLayer)
        self.comboBox_statsLayer.setFilters(
            QgsMapLayerProxyModel.PolygonLayer)

    def run_stats(self):
        self.progressBar_stats.setValue(0)
        self.label_progressStats.setText('')
        # noinspection PyArgumentList
        QApplication.processEvents()

        blurred_layer = self.comboBox_blurredLayer.currentLayer()
        stats_layer = self.comboBox_statsLayer.currentLayer()

        try:

            if not blurred_layer or not stats_layer:
                raise NoLayerProvidedException

            crs_blurred_layer = blurred_layer.crs()
            crs_stats_layer = stats_layer.crs()

            if crs_blurred_layer != crs_stats_layer:
                raise DifferentCrsException(
                    epsg1=crs_blurred_layer.authid(),
                    epsg2=crs_stats_layer.authid())

            if blurred_layer == stats_layer:
                raise NoLayerProvidedException

            if not blurred_layer or not stats_layer:
                raise NoLayerProvidedException

            nb_feature_stats = stats_layer.featureCount()
            nb_feature_blurred = blurred_layer.featureCount()
            features_stats = {}

            label_preparing = tr('Preparing index on the stats layer')
            label_creating = tr('Creating index on the stats layer')
            label_calculating = tr('Calculating')

            if Qgis.QGIS_VERSION_INT < 20700:
                self.label_progressStats.setText('%s 1/3' % label_preparing)

                for i, feature in enumerate(stats_layer.getFeatures()):
                    features_stats[feature.id()] = feature
                    percent = int((i + 1) * 100 / nb_feature_stats)
                    self.progressBar_stats.setValue(percent)
                    # noinspection PyArgumentList
                    QApplication.processEvents()

                self.label_progressStats.setText('%s 2/3' % label_creating)
                # noinspection PyArgumentList
                QApplication.processEvents()
                index = QgsSpatialIndex()
                for i, f in enumerate(stats_layer.getFeatures()):
                    index.insertFeature(f)

                    percent = int((i + 1) * 100 / nb_feature_stats)
                    self.progressBar_stats.setValue(percent)
                    # noinspection PyArgumentList
                    QApplication.processEvents()

                self.label_progressStats.setText('%s 3/3' % label_calculating)

            else:
                # If QGIS >= 2.7, we can speed up the spatial index.
                # From 1 min 15 to 7 seconds on my PC.
                self.label_progressStats.setText('%s 1/2' % label_creating)
                # noinspection PyArgumentList
                QApplication.processEvents()
                index = QgsSpatialIndex(stats_layer.getFeatures())
                self.label_progressStats.setText('%s 2/2' % label_calculating)

            # noinspection PyArgumentList
            QApplication.processEvents()
            self.tab = []
            for i, feature in enumerate(blurred_layer.getFeatures()):
                count = 0
                ids = index.intersects(feature.geometry().boundingBox())
                for unique_id in ids:
                    request = QgsFeatureRequest().setFilterFid(unique_id)
                    f = next(stats_layer.getFeatures(request))

                    if f.geometry().intersects(feature.geometry()):
                        count += 1
                self.tab.append(count)

                percent = int((i + 1) * 100 / nb_feature_blurred)
                self.progressBar_stats.setValue(percent)
                # noinspection PyArgumentList
                QApplication.processEvents()

            stats = Stats(self.tab)

            items_stats = [
                'Count(blurred),%d' % nb_feature_blurred,
                'Count(stats),%d' % nb_feature_stats,
                'Min,%d' % stats.min(),
                'Average,%f' % stats.average(),
                'Max,%d' % stats.max(), 'Median,%f' % stats.median(),
                'Range,%d' % stats.range(),
                'Variance,%f' % stats.variance(),
                'Standard deviation,%f' % stats.standard_deviation()
            ]

            self.tableWidget.clear()
            self.tableWidget.setColumnCount(2)
            labels = ['Parameters', 'Values']
            self.tableWidget.setHorizontalHeaderLabels(labels)
            self.tableWidget.setRowCount(len(items_stats))

            for i, item in enumerate(items_stats):
                s = item.split(',')
                self.tableWidget.setItem(i, 0, QTableWidgetItem(s[0]))
                self.tableWidget.setItem(i, 1, QTableWidgetItem(s[1]))
            self.tableWidget.resizeRowsToContents()

            self.draw_plot(self.tab)

        except GeoPublicHealthException as e:
            self.label_progressStats.setText('')
            display_message_bar(msg=e.msg, level=e.level, duration=e.duration)

    def save_table(self):

        if not self.tableWidget.rowCount():
            return False

        csv_string = 'parameter,values\n'

        for i in range(self.tableWidget.rowCount()):
            item_param = self.tableWidget.item(i, 0)
            item_value = self.tableWidget.item(i, 1)
            csv_string += \
                str(item_param.text()) + ',' + item_value.text() + '\n'

        last_directory = get_last_input_path()

        # noinspection PyArgumentList
        output_file, __ = QFileDialog.getSaveFileName(
            parent=self,
            caption=tr('Select file'),
            directory=last_directory,
            filter='CSV (*.csv)')

        if output_file:
            path = dirname(output_file)
            set_last_input_path(path)

            fh = open(output_file, 'w')
            fh.write(csv_string)
            fh.close()
            return True

    def save_y_values(self):

        if not self.tableWidget.rowCount():
            return False

        csv_string = 'parameter,values\n'

        for value in self.tab:
            csv_string += str(value) + '\n'

        last_directory = get_last_input_path()
        # noinspection PyArgumentList
        output_file, __ = QFileDialog.getSaveFileName(
            parent=self,
            caption=tr('Select file'),
            directory=last_directory,
            filter='CSV (*.csv)')

        if output_file:
            path = dirname(output_file)
            set_last_input_path(path)

            fh = open(output_file, 'w')
            fh.write(csv_string)
            fh.close()
            return True

    def draw_plot(self, data):
        # Creating the plot
        # create an axis
        ax = self.figure.add_subplot(111)
        # discards the old graph
        
        # plot data
        ax.plot(data, '*-')

        # ax.set_title('Number of intersections per entity')
        ax.set_xlabel('Blurred entity')
        ax.set_ylabel('Number of intersections')
        ax.grid()

        # refresh canvas
        self.canvas.draw()
コード例 #17
0
class BatchInputSelectionPanel(QWidget):

    valueChanged = pyqtSignal()

    def __init__(self, param, row, col, dialog):
        super(BatchInputSelectionPanel, self).__init__(None)
        self.param = param
        self.dialog = dialog
        self.row = row
        self.col = col
        self.horizontalLayout = QHBoxLayout(self)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setMargin(0)
        self.text = QLineEdit()
        self.text.setObjectName('text')
        self.text.setMinimumWidth(300)
        self.setValue('')
        self.text.editingFinished.connect(self.textEditingFinished)
        self.text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.horizontalLayout.addWidget(self.text)
        self.pushButton = QPushButton()
        self.pushButton.setText('…')
        self.pushButton.clicked.connect(self.showPopupMenu)
        self.horizontalLayout.addWidget(self.pushButton)
        self.setLayout(self.horizontalLayout)

    def _panel(self):
        return self.dialog.mainWidget()

    def _table(self):
        return self._panel().tblParameters

    def showPopupMenu(self):
        popupmenu = QMenu()

        if not (isinstance(self.param, QgsProcessingParameterMultipleLayers)
                and self.param.layerType == dataobjects.TYPE_FILE):
            selectLayerAction = QAction(
                QCoreApplication.translate('BatchInputSelectionPanel',
                                           'Select from Open Layers…'),
                self.pushButton)
            selectLayerAction.triggered.connect(self.showLayerSelectionDialog)
            popupmenu.addAction(selectLayerAction)

        selectFileAction = QAction(
            QCoreApplication.translate('BatchInputSelectionPanel',
                                       'Select from File System…'),
            self.pushButton)
        selectFileAction.triggered.connect(self.showFileSelectionDialog)
        popupmenu.addAction(selectFileAction)

        popupmenu.exec_(QCursor.pos())

    def showLayerSelectionDialog(self):
        layers = []
        if (isinstance(self.param, QgsProcessingParameterRasterLayer) or
            (isinstance(self.param, QgsProcessingParameterMultipleLayers)
             and self.param.layerType() == QgsProcessing.TypeRaster)):
            layers = QgsProcessingUtils.compatibleRasterLayers(
                QgsProject.instance())
        elif isinstance(self.param, QgsProcessingParameterVectorLayer):
            layers = QgsProcessingUtils.compatibleVectorLayers(
                QgsProject.instance())
        else:
            datatypes = [QgsProcessing.TypeVectorAnyGeometry]
            if isinstance(self.param, QgsProcessingParameterFeatureSource):
                datatypes = self.param.dataTypes()
            elif isinstance(self.param, QgsProcessingParameterMultipleLayers):
                datatypes = [self.param.layerType()]

            if QgsProcessing.TypeVectorAnyGeometry not in datatypes:
                layers = QgsProcessingUtils.compatibleVectorLayers(
                    QgsProject.instance(), datatypes)
            else:
                layers = QgsProcessingUtils.compatibleVectorLayers(
                    QgsProject.instance())

        dlg = MultipleInputDialog([layer.name() for layer in layers])
        dlg.exec_()

        def generate_layer_id(layer):
            # prefer layer name if unique
            if len([
                    l for l in layers
                    if l.name().lower() == layer.name().lower()
            ]) == 1:
                return layer.name()
            else:
                # otherwise fall back to layer id
                return layer.id()

        if dlg.selectedoptions is not None:
            selected = dlg.selectedoptions
            if len(selected) == 1:
                self.setValue(generate_layer_id(layers[selected[0]]))
            else:
                if isinstance(self.param,
                              QgsProcessingParameterMultipleLayers):
                    self.text.setText(';'.join(layers[idx].id()
                                               for idx in selected))
                else:
                    rowdif = len(selected) - (self._table().rowCount() -
                                              self.row)
                    for i in range(rowdif):
                        self._panel().addRow()
                    for i, layeridx in enumerate(selected):
                        self._table().cellWidget(
                            i + self.row, self.col).setValue(
                                generate_layer_id(layers[layeridx]))

    def showFileSelectionDialog(self):
        settings = QgsSettings()
        text = str(self.text.text())
        if os.path.isdir(text):
            path = text
        elif os.path.isdir(os.path.dirname(text)):
            path = os.path.dirname(text)
        elif settings.contains('/Processing/LastInputPath'):
            path = str(settings.value('/Processing/LastInputPath'))
        else:
            path = ''

        ret, selected_filter = QFileDialog.getOpenFileNames(
            self, self.tr('Select Files'), path, getFileFilter(self.param))
        if ret:
            files = list(ret)
            settings.setValue('/Processing/LastInputPath',
                              os.path.dirname(str(files[0])))
            for i, filename in enumerate(files):
                files[i] = dataobjects.getRasterSublayer(filename, self.param)
            if len(files) == 1:
                self.text.setText(files[0])
                self.textEditingFinished()
            else:
                if isinstance(self.param,
                              QgsProcessingParameterMultipleLayers):
                    self.text.setText(';'.join(str(f) for f in files))
                else:
                    rowdif = len(files) - (self._table().rowCount() - self.row)
                    for i in range(rowdif):
                        self._panel().addRow()
                    for i, f in enumerate(files):
                        self._table().cellWidget(i + self.row,
                                                 self.col).setValue(f)

    def textEditingFinished(self):
        self._value = self.text.text()
        self.valueChanged.emit()

    def value(self):
        return self._value

    def setValue(self, value):
        self._value = value
        if isinstance(value, QgsMapLayer):
            self.text.setText(value.name())
        else:  # should be basestring
            self.text.setText(value)
        self.valueChanged.emit()
コード例 #18
0
class DBTree(QTreeView):
    selectedItemChanged = pyqtSignal(object)

    def __init__(self, mainWindow):
        QTreeView.__init__(self, mainWindow)
        self.mainWindow = mainWindow

        self.setModel(DBModel(self))
        self.setHeaderHidden(True)
        self.setEditTriggers(QTreeView.EditKeyPressed
                             | QTreeView.SelectedClicked)

        self.setDragEnabled(True)
        self.setAcceptDrops(True)
        self.setDropIndicatorShown(True)

        self.doubleClicked.connect(self.addLayer)
        self.selectionModel().currentChanged.connect(self.currentItemChanged)
        self.expanded.connect(self.itemChanged)
        self.collapsed.connect(self.itemChanged)
        self.model().dataChanged.connect(self.modelDataChanged)
        self.model().notPopulated.connect(self.collapse)

    def refreshItem(self, item=None):
        if item is None:
            item = self.currentItem()
            if item is None:
                return
        self.model().refreshItem(item)

    def showSystemTables(self, show):
        pass

    def currentItem(self):
        indexes = self.selectedIndexes()
        if len(indexes) <= 0:
            return
        return self.model().getItem(indexes[0])

    def currentDatabase(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (DBPlugin, Schema, Table)):
            return item.database()
        return None

    def currentSchema(self):
        item = self.currentItem()
        if item is None:
            return

        if isinstance(item, (Schema, Table)):
            return item.schema()
        return None

    def currentTable(self):
        item = self.currentItem()

        if isinstance(item, Table):
            return item
        return None

    def newConnection(self):
        index = self.currentIndex()
        if not index.isValid() or not isinstance(index.internalPointer(),
                                                 PluginItem):
            return
        item = self.currentItem()
        self.mainWindow.invokeCallback(item.addConnectionActionSlot, index)

    def itemChanged(self, index):
        self.setCurrentIndex(index)
        self.selectedItemChanged.emit(self.currentItem())

    def modelDataChanged(self, indexFrom, indexTo):
        self.itemChanged(indexTo)

    def currentItemChanged(self, current, previous):
        self.itemChanged(current)

    def contextMenuEvent(self, ev):
        index = self.indexAt(ev.pos())
        if not index.isValid():
            return

        if index != self.currentIndex():
            self.itemChanged(index)

        item = self.currentItem()

        menu = QMenu(self)

        if isinstance(item, (Table, Schema)):
            menu.addAction(QCoreApplication.translate("DBTree", "Rename…"),
                           self.rename)
            menu.addAction(QCoreApplication.translate("DBTree", "Delete…"),
                           self.delete)

            if isinstance(item, Table) and item.canBeAddedToCanvas():
                menu.addSeparator()
                menu.addAction(self.tr("Add to Canvas"), self.addLayer)
                item.addExtraContextMenuEntries(menu)

        elif isinstance(item, DBPlugin):
            if item.database() is not None:
                menu.addAction(self.tr("Re-connect"), self.reconnect)
            menu.addAction(self.tr("Remove"), self.delete)

        elif not index.parent().isValid() and item.typeName() in ("spatialite",
                                                                  "gpkg"):
            menu.addAction(
                QCoreApplication.translate("DBTree", "New Connection…"),
                self.newConnection)

        if not menu.isEmpty():
            menu.exec_(ev.globalPos())

        menu.deleteLater()

    def rename(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.edit(self.currentIndex())

    def delete(self):
        item = self.currentItem()
        if isinstance(item, (Table, Schema)):
            self.mainWindow.invokeCallback(item.database().deleteActionSlot)
        elif isinstance(item, DBPlugin):
            self.mainWindow.invokeCallback(item.removeActionSlot)

    def addLayer(self):
        table = self.currentTable()
        if table is not None:
            layer = table.toMapLayer()
            layers = QgsProject.instance().addMapLayers([layer])
            if len(layers) != 1:
                QgsMessageLog.logMessage(
                    self.tr("%1 is an invalid layer - not loaded").replace(
                        "%1", layer.publicSource()))
                msgLabel = QLabel(
                    self.
                    tr("%1 is an invalid layer and cannot be loaded. Please check the <a href=\"#messageLog\">message log</a> for further info."
                       ).replace("%1", layer.publicSource()),
                    self.mainWindow.infoBar)
                msgLabel.setWordWrap(True)
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow().findChild(
                        QWidget, "MessageLog").show)
                msgLabel.linkActivated.connect(
                    self.mainWindow.iface.mainWindow().raise_)
                self.mainWindow.infoBar.pushItem(
                    QgsMessageBarItem(msgLabel, Qgis.Warning))

    def reconnect(self):
        db = self.currentDatabase()
        if db is not None:
            self.mainWindow.invokeCallback(db.reconnectActionSlot)
コード例 #19
0
ファイル: RectangleMapTool.py プロジェクト: k-itadaki/QGIS
class RectangleMapTool(QgsMapToolEmitPoint):

    rectangleCreated = pyqtSignal()
    deactivated = pyqtSignal()

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubberBand = QgsRubberBand(self.canvas,
                                        QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(2)

        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True

        self.showRect(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        if self.rectangle() is not None:
            self.rectangleCreated.emit()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        # True to update canvas
        self.rubberBand.addPoint(point4, True)
        self.rubberBand.show()

    def rectangle(self):
        if self.startPoint is None or self.endPoint is None:
            return None
        elif self.startPoint.x() == self.endPoint.x() or \
                self.startPoint.y() == self.endPoint.y():
            return None

        return QgsRectangle(self.startPoint, self.endPoint)

    def setRectangle(self, rect):
        if rect == self.rectangle():
            return False

        if rect is None:
            self.reset()
        else:
            self.startPoint = QgsPointXY(rect.xMaximum(), rect.yMaximum())
            self.endPoint = QgsPointXY(rect.xMinimum(), rect.yMinimum())
            self.showRect(self.startPoint, self.endPoint)
        return True

    def deactivate(self):
        QgsMapTool.deactivate(self)
        self.deactivated.emit()
コード例 #20
0
class IliExecutable(QObject, metaclass=AbstractQObjectMeta):
    SUCCESS = 0
    ERROR = 1000
    ILI2DB_NOT_FOUND = 1001

    stdout = pyqtSignal(str)
    stderr = pyqtSignal(str)
    process_started = pyqtSignal(str)
    process_finished = pyqtSignal(int, int)

    __done_pattern = re.compile(r"Info: \.\.\.([a-z]+ )?done")
    _result = None

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.filename = None
        self.tool = None
        self.configuration = self._create_config()
        _, self.encoding = locale.getlocale()

        # Lets python try to determine the default locale
        if not self.encoding:
            _, self.encoding = locale.getdefaultlocale()

        # This might be unset
        # (https://stackoverflow.com/questions/1629699/locale-getlocale-problems-on-osx)
        if not self.encoding:
            self.encoding = 'UTF8'

    @abstractmethod
    def _create_config(self) -> Ili2DbCommandConfiguration:
        """Creates the configuration that will be used by *run* method.

        :return: ili2db configuration"""
        pass

    def _get_ili2db_version(self):
        return self.configuration.db_ili_version

    def _args(self, hide_password):
        """Gets the list of ili2db arguments from configuration.

        :param bool hide_password: *True* to mask the password, *False* otherwise.
        :return: ili2db arguments list.
        :rtype: list
        """
        self.configuration.tool = self.tool

        return get_ili2db_args(self.configuration, hide_password)

    def _ili2db_jar_arg(self):
        ili2db_bin = get_ili2db_bin(self.tool, self._get_ili2db_version(),
                                    self.stdout, self.stderr)
        if not ili2db_bin:
            return self.ILI2DB_NOT_FOUND
        return ["-jar", ili2db_bin]

    def _escaped_arg(self, argument):
        if '"' in argument:
            argument = argument.replace('"', '"""')
        if ' ' in argument:
            argument = '"' + argument + '"'
        return argument

    def command(self, hide_password):
        ili2db_jar_arg = self._ili2db_jar_arg()
        args = self._args(hide_password)
        java_path = self._escaped_arg(
            get_java_path(self.configuration.base_configuration))
        command_args = ili2db_jar_arg + args

        valid_args = []
        for command_arg in command_args:
            valid_args.append(self._escaped_arg(command_arg))

        command = java_path + ' ' + ' '.join(valid_args)

        return command

    def command_with_password(self, edited_command):
        if '--dbpwd ******' in edited_command:
            args = self._args(False)
            i = args.index('--dbpwd')
            edited_command = edited_command.replace('--dbpwd ******',
                                                    '--dbpwd ' + args[i + 1])
        return edited_command

    def command_without_password(self, edited_command=None):
        if not edited_command:
            return self.command(True)
        regex = re.compile('--dbpwd [^ ]*')
        match = regex.match(edited_command)
        if match:
            edited_command = edited_command.replace(match.group(1),
                                                    '--dbpwd ******')
        return edited_command

    def run(self, edited_command=None):
        proc = QProcess()
        proc.readyReadStandardError.connect(
            functools.partial(self.stderr_ready, proc=proc))
        proc.readyReadStandardOutput.connect(
            functools.partial(self.stdout_ready, proc=proc))

        if not edited_command:
            ili2db_jar_arg = self._ili2db_jar_arg()
            if ili2db_jar_arg == self.ILI2DB_NOT_FOUND:
                return self.ILI2DB_NOT_FOUND
            args = self._args(False)
            java_path = get_java_path(self.configuration.base_configuration)
            proc.start(java_path, ili2db_jar_arg + args)
        else:
            proc.start(self.command_with_password(edited_command))

        if not proc.waitForStarted():
            proc = None

        if not proc:
            raise JavaNotFoundError()

        self.process_started.emit(
            self.command_without_password(edited_command))

        self._result = self.ERROR

        loop = QEventLoop()
        proc.finished.connect(loop.exit)
        loop.exec()

        self.process_finished.emit(proc.exitCode(), self._result)
        return self._result

    def _search_custom_pattern(self, text):
        pass

    def stderr_ready(self, proc):
        text = bytes(proc.readAllStandardError()).decode(self.encoding)

        if self.__done_pattern.search(text):
            self._result = self.SUCCESS

        self._search_custom_pattern(text)

        self.stderr.emit(text)

    def stdout_ready(self, proc):
        text = bytes(proc.readAllStandardOutput()).decode(self.encoding)
        self.stdout.emit(text)
コード例 #21
0
class QualityErrorDBUtils(QObject):

    progress_changed = pyqtSignal(int)  # Progress changed

    def __init__(self):
        QObject.__init__(self)

    def get_quality_error_connector(self,
                                    output_path,
                                    timestamp,
                                    load_layers=False):
        # TODO: should we support both new and existent gpkgs in this method? I guess so!
        self.progress_changed.emit(0)

        res, msg, output_path = QualityErrorDBUtils.get_quality_validation_output_path(
            output_path, timestamp)
        if not res:
            return False, msg, None

        db_file = os.path.join(output_path,
                               "Reglas_de_Calidad_{}.gpkg".format(timestamp))
        db = GPKGConnector(db_file)
        ili2db = Ili2DB()
        error_model = LADMColModelRegistry().model(
            LADMNames.QUALITY_ERROR_MODEL_KEY)

        configuration = ili2db.get_import_schema_configuration(
            db, [error_model.full_name()])
        res, msg = ili2db.import_schema(db, configuration)

        self.progress_changed.emit(50)

        if res:
            for catalog_key, catalog_xtf_path in error_model.get_catalogs(
            ).items():
                logger.info(
                    __name__,
                    "Importing catalog '{}' to quality error database...".
                    format(catalog_key))
                configuration = ili2db.get_import_data_configuration(
                    db, catalog_xtf_path)
                res_xtf, msg_xtf = ili2db.import_data(db, configuration)
                if not res_xtf:
                    logger.warning(
                        __name__,
                        "There was a problem importing catalog '{}'! Skipping..."
                        .format(catalog_key))
        else:
            return False, "There were errors creating the 'Errores Calidad' structure!", None

        self.progress_changed.emit(80)

        if getattr(db.names, "T_ID_F",
                   None) is None or db.names.T_ID_F is None:
            db.test_connection()  # Just to build the names object

        if load_layers:
            names = db.names
            layers = {
                names.ERR_QUALITY_ERROR_T: None,
                names.ERR_RULE_TYPE_T: None,
                names.ERR_ERROR_TYPE_T: None,
                names.ERR_POINT_T: None,
                names.ERR_LINE_T: None,
                names.ERR_POLYGON_T: None,
                names.ERR_METADATA_T: None,
                names.ERR_ERROR_STATE_D: None
            }
            app.core.get_layers(
                db,
                layers,
                load=True,
                group=QualityErrorDBUtils.get_quality_error_group(timestamp))

        self.progress_changed.emit(100)

        return res, msg, None if not res else db

    @staticmethod
    def get_quality_validation_output_path(base_output_path, timestamp):
        res, msg = True, 'Success'
        if not os.path.exists(base_output_path):
            base_output_path = tempfile.gettempdir()
            logger.warning(
                __name__,
                QCoreApplication.translate(
                    "QualityRuleEngine",
                    "Output dir doesn't exist! Using now '{}'").format(
                        base_output_path))

        output_path = os.path.join(base_output_path,
                                   "Reglas_de_Calidad_{}".format(timestamp))
        if not os.path.exists(output_path):
            try:
                os.makedirs(output_path)
            except PermissionError as e:
                res = False
                msg = QCoreApplication.translate(
                    "QualityRuleEngine",
                    "Output dir '{}' is read-only!").format(base_output_path)
                logger.critical(__name__, msg)
                output_path = None

        return res, msg, output_path

    @staticmethod
    def save_errors(db_qr,
                    rule_code,
                    error_code,
                    error_data,
                    target_layer,
                    ili_name=None):
        """
        Save error data in the quality error model by error_code, which means that you should call this function once
        per error type. Accepts both, spatial and non-spatial errors.

        :param db_qr: DB Connector
        :param rule_code: Rule code as specified in the external catalogs.
        :param error_code: Error code as specified in the external catalogs.
        :param error_data: Dict of lists:
                               {'geometries': [geometries], 'data': [obj_uuids, rel_obj_uuids, values, details, state]}
                               Note: this dict will always have 2 elements.
                               Note 2: For geometry errors, this dict will always have the same number of elements in
                                       each of the two lists (and the element order matters!).
                               Note 3: For geometryless errors, the 'geometries' value won't be even read.
        :param target_layer: Value of EnumQualityRuleType.
        :param ili_name: Interlis name of the class where the object uuids can be found.
        :return: Tuple (res: boolean, msg: string)
        """
        if not hasattr(db_qr.names, "T_ID_F"):
            db_qr.test_connection()

        names = db_qr.names
        layers = {
            names.ERR_QUALITY_ERROR_T: None,
            names.ERR_RULE_TYPE_T: None,
            names.ERR_ERROR_TYPE_T: None
        }

        if target_layer == EnumQualityRuleType.POINT:
            layers[names.ERR_POINT_T] = None
        elif target_layer == EnumQualityRuleType.LINE:
            layers[names.ERR_LINE_T] = None
        elif target_layer == EnumQualityRuleType.POLYGON:
            layers[names.ERR_POLYGON_T] = None

        app.core.get_layers(db_qr, layers, load=True)

        if not layers:
            return False, "At least one layer was not found in the quality error db!"

        # We do now a soon check of rule code and error code to stop if cannot be found
        fids = LADMData.get_fids_from_key_values(layers[names.ERR_RULE_TYPE_T],
                                                 names.ERR_RULE_TYPE_T_CODE_F,
                                                 [rule_code])
        if not fids:
            return False, "There was a problem saving quality error data. The rule '{}' cannot be found in the database. Is that rule in any registered catalog?".format(
                rule_code)
        rule_code_id = fids[0]

        fids = LADMData.get_fids_from_key_values(
            layers[names.ERR_ERROR_TYPE_T], names.ERR_ERROR_TYPE_T_CODE_F,
            [error_code])
        if not fids:
            return False, "There was a problem saving quality error data. The error '{}' cannot be found in the database. Is that error in any registered catalog?".format(
                error_code)
        error_code_id = fids[0]

        qr_error_layer = layers[names.ERR_QUALITY_ERROR_T]

        # First we deal with geometries
        geom_layer = None
        idx_geometry_fk = None
        geom_t_ids = list()  # To store fks (geom t_id)
        if target_layer == EnumQualityRuleType.POINT:
            geom_layer = layers[names.ERR_POINT_T]
            idx_geometry_fk = qr_error_layer.fields().indexOf(
                names.ERR_QUALITY_ERROR_T_POINT_F)
        elif target_layer == EnumQualityRuleType.LINE:
            geom_layer = layers[names.ERR_LINE_T]
            idx_geometry_fk = qr_error_layer.fields().indexOf(
                names.ERR_QUALITY_ERROR_T_LINE_F)
        elif target_layer == EnumQualityRuleType.POLYGON:
            geom_layer = layers[names.ERR_POLYGON_T]
            idx_geometry_fk = qr_error_layer.fields().indexOf(
                names.ERR_QUALITY_ERROR_T_POLYGON_F)

        if geom_layer:
            idx_t_ili_tid_geom = geom_layer.fields().indexOf(names.T_ILI_TID_F)
            geometries = error_data['geometries']

            features = list()
            for geometry in geometries:
                new_feature = QgsVectorLayerUtils().createFeature(
                    geom_layer, geometry,
                    {idx_t_ili_tid_geom: str(uuid.uuid4())})
                features.append(new_feature)

            res_geom, out_features = geom_layer.dataProvider().addFeatures(
                features)
            if res_geom:
                geom_t_ids = [
                    out_feature[names.T_ID_F] for out_feature in out_features
                ]
            else:
                return False, "There was a problem saving error geometries for error code '{}'!".format(
                    error_code)

        # Now we deal with the alphanumeric data
        idx_t_ili_tid = qr_error_layer.fields().indexOf(names.T_ILI_TID_F)
        idx_rule_type = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_RULE_TYPE_F)
        idx_error_type = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_ERROR_TYPE_F)
        idx_object_ids = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_OBJECT_IDS_F)
        idx_rel_object_ids = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_RELATED_OBJECT_IDS_F)
        idx_values = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_VALUES_F)
        idx_ili_name = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_ILI_NAME_F)
        idx_details = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_DETAILS_F)
        idx_error_state = qr_error_layer.fields().indexOf(
            names.ERR_QUALITY_ERROR_T_ERROR_STATE_F)

        features = list()
        for i, data in enumerate(error_data['data']):
            attr_map = {
                idx_t_ili_tid: str(uuid.uuid4()),
                idx_rule_type: rule_code_id,
                idx_error_type: error_code_id,
                idx_object_ids: data[0],
                idx_rel_object_ids: data[1],
                idx_values: data[2],
                idx_details: data[3],
                idx_ili_name: ili_name,
                idx_error_state: data[4]
            }
            if geom_layer:
                attr_map[idx_geometry_fk] = geom_t_ids[
                    i]  # We take advantage of the preserved order here

            features.append(QgsVectorLayerUtils().createFeature(
                qr_error_layer, attributes=attr_map))

        res_data, out_features = qr_error_layer.dataProvider().addFeatures(
            features)
        if not res_data:
            return False, "There was a problem saving error alphanumeric data for error code '{}'! Details from the provider: {}".format(
                error_code,
                qr_error_layer.dataProvider().lastError())

        return True, "Success!"

    @staticmethod
    def save_metadata(db_qr, metadata):
        """
        :param db_qr: DBConnector
        :param metadata: Dict with the following information:
                            QR_METADATA_TOOL
                            QR_METADATA_DATA_SOURCE
                            QR_METADATA_TOLERANCE
                            QR_METADATA_TIMESTAMP
                            QR_METADATA_RULES
                            QR_METADATA_OPTIONS
                            QR_METADATA_PERSON
        :return: tuple (bool with the result, string with a descriptive message)
        """
        if not hasattr(db_qr.names, "T_ID_F"):
            db_qr.test_connection()

        names = db_qr.names
        layers = {names.ERR_METADATA_T: None, names.ERR_RULE_TYPE_T: None}

        app.core.get_layers(db_qr, layers, load=True)

        if not layers:
            return False, "At least one layer was not found in the QR DB!"

        metadata_layer = layers[names.ERR_METADATA_T]

        idx_t_ili_tid = metadata_layer.fields().indexOf(names.T_ILI_TID_F)
        idx_date = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_VALIDATION_DATE_F)
        idx_data_source = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_DATA_SOURCE_F)
        idx_tool = metadata_layer.fields().indexOf(names.ERR_METADATA_T_TOOL_F)
        idx_person = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_PERSON_F)
        idx_tolerance = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_TOLERANCE_F)
        idx_rules = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_RULES_F)
        idx_options = metadata_layer.fields().indexOf(
            names.ERR_METADATA_T_RULE_OPTIONS_F)

        # Initially, the metadata table had references to QR types, but as soon as we
        # wanted to save them as ARRAYs in GPKG or PG, ili2db said "no, I cannot handle that."
        # See https://github.com/claeis/ili2db/issues/438
        #dict_rules = {f[names.ERR_ERROR_TYPE_T_CODE_F]:f[names.T_ID_F] for f in layers[names.ERR_RULE_TYPE_T].getFeatures()}
        #list_rules = str([dict_rules[qr_key] for qr_key in metadata[QR_METADATA_RULES] if qr_key in dict_rules])

        time_structure = time.strptime(metadata[QR_METADATA_TIMESTAMP],
                                       '%Y%m%d_%H%M%S')
        iso_timestamp = time.strftime('%Y-%m-%dT%H:%M:%S', time_structure)

        attr_map = {
            idx_t_ili_tid:
            str(uuid.uuid4()),
            idx_date:
            QDateTime().fromString(iso_timestamp, Qt.ISODate),
            idx_data_source:
            metadata[QR_METADATA_DATA_SOURCE],
            idx_tool:
            metadata[QR_METADATA_TOOL],
            idx_person:
            metadata[QR_METADATA_PERSON],
            idx_tolerance:
            metadata[QR_METADATA_TOLERANCE],
            idx_rules:
            "{}: {}".format(len(metadata[QR_METADATA_RULES]),
                            str(metadata[QR_METADATA_RULES])),
            idx_options:
            str(metadata[QR_METADATA_OPTIONS])
        }

        metadata_layer.dataProvider().addFeature(
            QgsVectorLayerUtils().createFeature(metadata_layer,
                                                attributes=attr_map))

        logger.info(__name__, "Quality rules metadata successfully saved!")
        return True, "Success!"

    @staticmethod
    def get_quality_error_group(timestamp, create_if_non_existent=True):
        root = QgsProject.instance().layerTreeRoot()
        prefix = TranslatableConfigStrings.get_translatable_config_strings(
        )[ERROR_LAYER_GROUP_PREFIX]
        group_name = "{} {}".format(prefix, timestamp)

        group = root.findGroup(group_name)
        if group is None and create_if_non_existent:
            group = root.insertGroup(0, group_name)
            ilg = qgis.utils.plugins['InvisibleLayersAndGroups']
            ilg.hideGroup(group)
            group.setExpanded(False)

        return group

    @staticmethod
    def get_quality_error_group_names():
        root = QgsProject.instance().layerTreeRoot()
        prefix = TranslatableConfigStrings.get_translatable_config_strings(
        )[ERROR_LAYER_GROUP_PREFIX]
        error_group_names = list()

        for group in root.findGroups(False):
            if group.name().startswith(prefix):
                error_group_names.append(group.name())

        return error_group_names

    @staticmethod
    def remove_quality_error_group(timestamp):
        group = QualityErrorDBUtils.get_quality_error_group(timestamp, False)
        if group:
            parent = group.parent()
            parent.removeChildNode(group)
コード例 #22
0
ファイル: updater.py プロジェクト: skylning/Roam
class UpdateWorker(QObject):
    projectUpdateStatus = pyqtSignal(str, str)
    projectInstalled = pyqtSignal(str)

    def __init__(self, basefolder):
        super(UpdateWorker, self).__init__()
        self.server = None
        self.basefolder = basefolder
        self.projectUpdateStatus.connect(self.status_updated)

    def check_url_found(self, url):
        url = quote_url(url)
        try:
            result = urlopen(url)
            return result.code == 200
        except urllib.error.HTTPError as ex:
            return False

    def fetch_data(self, rootfolder, filename, serverurl):
        """
        Download the update zip file for the project from the server
        """
        serverurl = add_slash(serverurl)

        tempfolder = os.path.join(rootfolder, "_updates")
        if not os.path.exists(tempfolder):
            os.mkdir(tempfolder)

        filename = "{}.zip".format(filename)
        url = urlparse.urljoin(serverurl, "projects/{}".format(filename))
        zippath = os.path.join(tempfolder, filename)
        if not self.check_url_found(url):
            yield "Skipping data download"
            yield "Done"
            return

        roam.utils.info("Downloading data zip from {}".format(url))
        try:
            for status in download_file(url, zippath):
                yield status
        except UpdateExpection as ex:
            roam.utils.exception("Error in update for project")
            yield "Error in downloading data"
            return

        yield "Extracting data.."
        with zipfile.ZipFile(zippath, "r") as z:
            members = z.infolist()
            for i, member in enumerate(members):
                z.extract(member, rootfolder)
                roam.utils.debug("Extracting: {}".format(member.filename))

        yield "Done"

    def status_updated(self, project, status):
        roam.utils.debug(project + ": " + status)

    def run(self):
        while True:
            project, version, server, is_new = forupdate.get()
            datadate = project["data_date"]
            datafolder = os.path.join(self.basefolder, "_data")
            if datadate != roam.config.settings.get("data_date", None) or not os.path.exists(datafolder):
                self.projectUpdateStatus.emit("Data", "Downloading Data..")
                for status in self.fetch_data(self.basefolder, "_data", server):
                    self.projectUpdateStatus.emit("Data", status)
                self.projectUpdateStatus.emit("Data", "Installed")
                roam.config.settings["data_date"] = datadate
                roam.config.save()
            else:
                roam.utils.info("Data download skipped. Already up to date.")

            if is_new:
                name = project['name']
                self.projectUpdateStatus.emit(name, "Installing")
                for status in install_project(project, self.basefolder, server):
                    self.projectUpdateStatus.emit(name, status)
                self.projectUpdateStatus.emit(name, "Installed")
                self.projectInstalled.emit(name)
            else:
                name = project['name']
                self.projectUpdateStatus.emit(name, "Updating")
                for status in install_project(project, self.basefolder, server, updateMode=True):
                    self.projectUpdateStatus.emit(name, status)
                self.projectUpdateStatus.emit(name, "Complete")
コード例 #23
0
class ExtentSelectorDialog(QDialog, FORM_CLASS):
    """Dialog for letting user determine analysis extents."""

    extent_defined = pyqtSignal(QgsRectangle, QgsCoordinateReferenceSystem)
    clear_extent = pyqtSignal()
    extent_selector_closed = pyqtSignal()

    def __init__(self, iface, parent=None, extent=None, crs=None):
        """Constructor for the dialog.

        :param iface: A Quantum GIS QgisAppInterface instance.
        :type iface: QgisAppInterface

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param extent: Extent of the user's preferred analysis area.
        :type extent: QgsRectangle

        :param crs: CRS for user defined analysis extent.
        :type crs: QgsCoordinateReferenceSystem
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)

        icon = resources_path('img', 'icons', 'set-extents-tool.svg')
        self.setWindowIcon(QIcon(icon))

        self.iface = iface
        self.parent = parent
        self.canvas = iface.mapCanvas()
        self.previous_map_tool = None
        # Prepare the map tool
        self.tool = RectangleMapTool(self.canvas)
        self.previous_map_tool = self.canvas.mapTool()

        if extent is None:
            # Use the current map canvas extents as a starting point
            self.tool.set_rectangle(self.canvas.extent())
        else:

            if isinstance(extent, QgsGeometry):
                # In InaSAFE V4, the extent is a QgsGeometry.
                # This like a hack to transform a geometry to a rectangle.
                extent = wkt_to_rectangle(extent.asWkt())

            # Ensure supplied extent is in current canvas crs
            transform = QgsCoordinateTransform(
                crs,
                self.canvas.mapSettings().destinationCrs(),
                QgsProject.instance())
            transformed_extent = transform.transformBoundingBox(extent)
            self.tool.set_rectangle(transformed_extent)

        self._populate_coordinates()

        # Observe inputs for changes
        self.x_minimum.valueChanged.connect(self._coordinates_changed)
        self.y_minimum.valueChanged.connect(self._coordinates_changed)
        self.x_maximum.valueChanged.connect(self._coordinates_changed)
        self.y_maximum.valueChanged.connect(self._coordinates_changed)

        # Draw the rubberband
        self._coordinates_changed()

        # Wire up button events
        self.capture_button.clicked.connect(self.start_capture)
        # Handle cancel
        cancel_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Cancel)
        cancel_button.clicked.connect(self.reject)
        # Make sure to reshow this dialog when rectangle is captured
        self.tool.rectangle_created.connect(self.stop_capture)
        # Setup ok button
        self.ok_button = self.button_box.button(QtWidgets.QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)
        # Set up context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)
        # Reset / Clear button
        clear_button = self.button_box.button(QtWidgets.QDialogButtonBox.Reset)
        clear_button.setText(self.tr('Clear'))
        clear_button.clicked.connect(self.clear)

        # Populate the bookmarks list and connect the combobox
        self._populate_bookmarks_list()
        self.bookmarks_list.currentIndexChanged.connect(
            self.bookmarks_index_changed)

        # Reinstate the last used radio button
        mode = setting('analysis_extents_mode', HAZARD_EXPOSURE_VIEW)

        if mode == HAZARD_EXPOSURE_VIEW:
            self.hazard_exposure_view_extent.setChecked(True)
        elif mode == EXPOSURE:
            self.exposure_only.setChecked(True)
        elif mode == HAZARD_EXPOSURE:
            self.hazard_exposure_only.setChecked(True)
        elif mode == HAZARD_EXPOSURE_BOOKMARK:
            self.hazard_exposure_bookmark.setChecked(True)
        elif mode == HAZARD_EXPOSURE_BOUNDINGBOX:
            self.hazard_exposure_user_extent.setChecked(True)

        self.show_warnings.setChecked(
            setting('show_extent_warnings', True, bool))
        self.show_confirmations.setChecked(
            setting('show_extent_confirmations', True, bool))

    @pyqtSlot(bool)  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the main stacked widget.

        .. versionadded: 3.2.1

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user.

        .. versionadded:: 3.2.1
        """
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = extent_selector_help()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)

    def start_capture(self):
        """Start capturing the rectangle."""
        previous_map_tool = self.canvas.mapTool()
        if previous_map_tool != self.tool:
            self.previous_map_tool = previous_map_tool
        self.canvas.setMapTool(self.tool)
        self.hide()

    def stop_capture(self):
        """Stop capturing the rectangle and reshow the dialog."""
        self._populate_coordinates()
        self.canvas.setMapTool(self.previous_map_tool)
        self.show()

    def clear(self):
        """Clear the currently set extent."""
        self.tool.reset()
        self._populate_coordinates()
        # Revert to using hazard, exposure and view as basis for analysis
        self.hazard_exposure_view_extent.setChecked(True)

    def reject(self):
        """User rejected the rectangle."""
        self.canvas.unsetMapTool(self.tool)
        if self.previous_map_tool != self.tool:
            self.canvas.setMapTool(self.previous_map_tool)
        self.tool.reset()
        self.extent_selector_closed.emit()
        super(ExtentSelectorDialog, self).reject()

    def accept(self):
        """User accepted the rectangle."""
        mode = None
        if self.hazard_exposure_view_extent.isChecked():
            mode = HAZARD_EXPOSURE_VIEW
        elif self.exposure_only.isChecked():
            mode = EXPOSURE
        elif self.hazard_exposure_only.isChecked():
            mode = HAZARD_EXPOSURE
        elif self.hazard_exposure_bookmark.isChecked():
            mode = HAZARD_EXPOSURE_BOOKMARK
        elif self.hazard_exposure_user_extent.isChecked():
            mode = HAZARD_EXPOSURE_BOUNDINGBOX

        set_setting('analysis_extents_mode', mode)

        self.canvas.unsetMapTool(self.tool)
        if self.previous_map_tool != self.tool:
            self.canvas.setMapTool(self.previous_map_tool)

        if not self.tool.rectangle().isEmpty():
            self.extent_defined.emit(
                self.tool.rectangle(),
                self.canvas.mapSettings().destinationCrs())
            extent = QgsGeometry.fromRect(self.tool.rectangle())
            LOGGER.info('Requested extent : {wkt}'.format(wkt=extent.asWkt()))
        else:
            self.clear_extent.emit()

        # State handlers for showing warning message bars
        set_setting('show_extent_warnings', self.show_warnings.isChecked())
        set_setting('show_extent_confirmations',
                    self.show_confirmations.isChecked())

        self.tool.reset()
        self.extent_selector_closed.emit()
        super(ExtentSelectorDialog, self).accept()

    def _are_coordinates_valid(self):
        """Check if the coordinates are valid.

        :return: True if coordinates are valid otherwise False.
        :type: bool
        """
        try:
            QgsPointXY(self.x_minimum.value(), self.y_maximum.value())
            QgsPointXY(self.x_maximum.value(), self.y_minimum.value())
        except ValueError:
            return False

        return True

    def _coordinates_changed(self):
        """Handle a change in the coordinate input boxes."""
        if self._are_coordinates_valid():
            point1 = QgsPointXY(self.x_minimum.value(), self.y_maximum.value())
            point2 = QgsPointXY(self.x_maximum.value(), self.y_minimum.value())
            rect = QgsRectangle(point1, point2)

            self.tool.set_rectangle(rect)

    def _populate_coordinates(self):
        """Update the UI with the current active coordinates."""
        rect = self.tool.rectangle()
        self.blockSignals(True)
        if not rect.isEmpty():
            self.x_minimum.setValue(rect.xMinimum())
            self.y_minimum.setValue(rect.yMinimum())
            self.x_maximum.setValue(rect.xMaximum())
            self.y_maximum.setValue(rect.yMaximum())
        else:
            self.x_minimum.clear()
            self.y_minimum.clear()
            self.x_maximum.clear()
            self.y_maximum.clear()
        self.blockSignals(False)

    def bookmarks_index_changed(self):
        """Update the UI when the bookmarks combobox has changed."""
        index = self.bookmarks_list.currentIndex()
        if index >= 0:
            self.tool.reset()
            rectangle = self.bookmarks_list.itemData(index)
            self.tool.set_rectangle(rectangle)
            self.canvas.setExtent(rectangle)
            self.ok_button.setEnabled(True)
        else:
            self.ok_button.setDisabled(True)

    def on_hazard_exposure_view_extent_toggled(self, enabled):
        """Handler for hazard/exposure/view radiobutton toggle.

        :param enabled: The status of the radiobutton.
        :type enabled: bool
        """
        if enabled:
            self.tool.reset()
            self._populate_coordinates()

    def on_hazard_exposure_only_toggled(self, enabled):
        """Handler for hazard/exposure radiobutton toggle.

        :param enabled: The status of the radiobutton.
        :type enabled: bool
        """
        if enabled:
            self.tool.reset()
            self._populate_coordinates()

    def on_hazard_exposure_bookmark_toggled(self, enabled):
        """Update the UI when the user toggles the bookmarks radiobutton.

        :param enabled: The status of the radiobutton.
        :type enabled: bool
        """
        if enabled:
            self.bookmarks_index_changed()
        else:
            self.ok_button.setEnabled(True)
        self._populate_coordinates()

    def _populate_bookmarks_list(self):
        """Read the sqlite database and populate the bookmarks list.

        If no bookmarks are found, the bookmarks radio button will be disabled
        and the label will be shown indicating that the user should add
        bookmarks in QGIS first.

        Every bookmark are reprojected to mapcanvas crs.
        """
        # Connect to the QGIS sqlite database and check if the table exists.
        # noinspection PyArgumentList
        db_file_path = QgsApplication.qgisUserDatabaseFilePath()
        db = sqlite3.connect(db_file_path)
        cursor = db.cursor()
        cursor.execute('SELECT COUNT(*) '
                       'FROM sqlite_master '
                       'WHERE type=\'table\' '
                       'AND name=\'tbl_bookmarks\';')

        number_of_rows = cursor.fetchone()[0]
        if number_of_rows > 0:
            cursor.execute('SELECT * ' 'FROM tbl_bookmarks;')
            bookmarks = cursor.fetchall()

            canvas_crs = self.canvas.mapSettings().destinationCrs()

            for bookmark in bookmarks:
                name = bookmark[1]
                srid = bookmark[7]
                rectangle = QgsRectangle(bookmark[3], bookmark[4], bookmark[5],
                                         bookmark[6])

                if srid != canvas_crs.srsid():
                    transform = QgsCoordinateTransform(
                        QgsCoordinateReferenceSystem(srid), canvas_crs,
                        QgsProject.instance())
                    try:
                        rectangle = transform.transform(rectangle)
                    except QgsCsException:
                        rectangle = QgsRectangle()

                if rectangle.isEmpty():
                    pass

                self.bookmarks_list.addItem(name, rectangle)
        if self.bookmarks_list.currentIndex() >= 0:
            self.create_bookmarks_label.hide()
        else:
            self.create_bookmarks_label.show()
            self.hazard_exposure_bookmark.setDisabled(True)
            self.bookmarks_list.hide()
コード例 #24
0
ファイル: updater.py プロジェクト: skylning/Roam
class ProjectUpdater(QObject):
    """
    Object to handle reporting when new versions of projects are found on the update server.

    Emits foundProjects when a new projects are found
    """
    foundProjects = pyqtSignal(list, list)
    projectUpdateStatus = pyqtSignal(str, str)
    projectInstalled = pyqtSignal(str)

    def __init__(self, server=None, projects_base=''):
        super(ProjectUpdater, self).__init__()
        self.updatethread = None
        self.server = server
        self.net = QgsNetworkAccessManager.instance()
        self.projects_base = projects_base
        self.create_worker()

    def quit(self):
        self.updatethread.quit()

    def create_worker(self):
        self.updatethread = QThread()
        self.worker = UpdateWorker(self.projects_base)
        self.worker.moveToThread(self.updatethread)

        self.worker.projectUpdateStatus.connect(self.projectUpdateStatus)
        self.worker.projectInstalled.connect(self.projectInstalled)
        self.updatethread.started.connect(self.worker.run)

    @property
    def configurl(self):
        url = urlparse.urljoin(add_slash(self.server), "projects/roam.txt")
        print("URL", url)
        return url

    def check_updates(self, server, installedprojects):
        if not server:
            return

        self.server = server
        req = QNetworkRequest(QUrl(self.configurl))
        reply = self.net.get(req)
        reply.finished.connect(functools.partial(self.list_versions, reply, installedprojects))

    def update_server(self, newserverurl, installedprojects):
        self.check_updates(newserverurl, installedprojects)

    def list_versions(self, reply, installedprojects):
        if reply.error() == QNetworkReply.NoError:
            content = reply.readAll().data()
            serverversions = parse_serverprojects(content)
            updateable = list(updateable_projects(installedprojects, serverversions))
            new = list(new_projects(installedprojects, serverversions))
            print(updateable)
            print(new)
            if updateable or new:
                self.foundProjects.emit(updateable, new)
        else:
            msg = "Error in network request for projects: {}".format(reply.errorString())
            roam.utils.warning(msg)

    def update_project(self, projectinfo):
        self.projectUpdateStatus.emit(projectinfo['name'], "Pending")
        forupdate.put((projectinfo, projectinfo['version'], self.server, False))
        self.updatethread.start()

    def install_project(self, projectinfo):
        self.projectUpdateStatus.emit(projectinfo['name'], "Pending")
        is_new = True
        forupdate.put((projectinfo, projectinfo['version'], self.server, is_new))
        self.updatethread.start()
コード例 #25
0
class ReferencedTableEditor(QWidget):
    referenced_table_changed = pyqtSignal(str)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi()

        self.cbo_ref_table.setInsertPolicy(QComboBox.InsertAlphabetically)

        self.cbo_ref_table.currentIndexChanged[str].connect(self._on_ref_table_changed)

        # Tables that will be omitted from the referenced table list
        self._omit_ref_tables = []

    def add_omit_table(self, table):
        """
        Add a table name that will be omitted from the list of referenced
        tables.
        :param table: Table name that will be omitted
        :type table: str
        """
        if not table in self._omit_ref_tables:
            self._omit_ref_tables.append(table)

    def add_omit_tables(self, tables):
        """
        Add a list of tables that will be omitted from the list of referenced
        tables.
        :param tables: Table names to be omitted.
        :type tables: list
        """
        for t in tables:
            self.add_omit_table(t)

    @property
    def omit_tables(self):
        """
        :return: Returns a list of tables that are to be omitted from the
        list of referenced tables.
        """
        return self._omit_ref_tables

    def setupUi(self):
        self.setObjectName("ReferencedTableEditor")
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setVerticalSpacing(10)
        self.gridLayout.setObjectName("gridLayout")
        self.label_2 = QLabel(self)
        self.label_2.setMaximumSize(QSize(100, 16777215))
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.cbo_source_field = QComboBox(self)
        self.cbo_source_field.setMinimumSize(QSize(0, 30))
        self.cbo_source_field.setObjectName("cbo_source_field")
        self.gridLayout.addWidget(self.cbo_source_field, 2, 1, 1, 1)
        self.label = QLabel(self)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
        self.label_3 = QLabel(self)
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
        self.cbo_referencing_col = QComboBox(self)
        self.cbo_referencing_col.setMinimumSize(QSize(0, 30))
        self.cbo_referencing_col.setObjectName("cbo_referencing_col")
        self.gridLayout.addWidget(self.cbo_referencing_col, 3, 1, 1, 1)
        self.cbo_ref_table = QComboBox(self)
        self.cbo_ref_table.setMinimumSize(QSize(0, 30))
        self.cbo_ref_table.setObjectName("cbo_ref_table")
        self.gridLayout.addWidget(self.cbo_ref_table, 1, 1, 1, 1)

        self.label_2.setText(QApplication.translate("ReferencedTableEditor",
                                                    "References"))
        self.label.setText(QApplication.translate("ReferencedTableEditor",
                                                  "Data source field"))
        self.label_3.setText(QApplication.translate("ReferencedTableEditor",
                                                    "Referencing"))

        self._current_profile = current_profile()
        self._current_profile_tables = []

        if self._current_profile is not None:
            self._current_profile_tables = self._current_profile.table_names()

        # Connect signals
        QMetaObject.connectSlotsByName(self)
        self.cbo_ref_table.currentIndexChanged[str].connect(self._load_source_table_fields)

    @pyqtSlot(str)
    def on_data_source_changed(self, data_source_name):
        """
        Loads data source fields for the given data source name.
        """
        self.load_data_source_fields(data_source_name)

    def _on_ref_table_changed(self, table):
        """
        Raise signal when the referenced table changes.
        :param table: Selected table name.
        :type table: str
        """
        self.referenced_table_changed.emit(table)

    def properties(self):
        """
        :returns: Returns the user-defined mapping of linked table and column
        pairings.
        :rtype: LinkedTableProps
        """
        l_table = self.cbo_ref_table.currentText()
        s_field = self.cbo_source_field.currentText()
        l_field = self.cbo_referencing_col.currentText()

        return LinkedTableProps(linked_table=l_table,
                                source_field=s_field,
                                linked_field=l_field)

    def set_properties(self, table_props):
        """
        Sets the combo selection based on the text in the linked table
        object properties.
        :param table_props: Object containing the linked table information.
        :type table_props: LinkedTableProps
        """
        setComboCurrentIndexWithText(self.cbo_ref_table, table_props.linked_table)
        setComboCurrentIndexWithText(self.cbo_source_field, table_props.source_field)
        setComboCurrentIndexWithText(self.cbo_referencing_col, table_props.linked_field)

    def load_data_source_fields(self, data_source_name):
        """
        Load fields/columns of the given data source.
        """
        if data_source_name == "":
            self.clear()

            return

        columns_names = table_column_names(data_source_name)

        if len(columns_names) == 0:
            return

        self.cbo_source_field.clear()
        self.cbo_source_field.addItem("")
        self.cbo_source_field.addItems(columns_names)

    def clear(self):
        """
        Resets combo box selections.
        """
        self._reset_combo_index(self.cbo_ref_table)
        self._reset_combo_index(self.cbo_referencing_col)
        self._reset_combo_index(self.cbo_source_field)

    def _reset_combo_index(self, combo):
        if combo.count > 0:
            combo.setCurrentIndex(0)

    def reset_referenced_table(self):
        self._reset_combo_index(self.cbo_ref_table)

    def load_link_tables(self, reg_exp=None, source=TABLES | VIEWS):
        self.cbo_ref_table.clear()
        self.cbo_ref_table.addItem("")

        ref_tables = []
        source_tables = []
        # Table source
        if (TABLES & source) == TABLES:
            ref_tables.extend(pg_tables(exclude_lookups=True))

            for t in ref_tables:
                # Ensure we are dealing with tables in the current profile
                if not t in self._current_profile_tables:
                    continue

                # Assert if the table is in the list of omitted tables
                if t in self._omit_ref_tables:
                    continue

                if not reg_exp is None:
                    if reg_exp.indexIn(t) >= 0:
                        source_tables.append(t)
                else:
                    source_tables.append(t)

        # View source
        if (VIEWS & source) == VIEWS:
            profile_user_views = profile_and_user_views(self._current_profile)
            source_tables = source_tables + profile_user_views

        self.cbo_ref_table.addItems(source_tables)

    def _load_source_table_fields(self, sel):
        self.cbo_referencing_col.clear()
        data_source_index = self.cbo_source_field.currentIndex()
        self.on_data_source_changed(
            self.cbo_source_field.itemData(data_source_index)
        )

        if not sel:
            return

        columns_names = table_column_names(sel)

        self.cbo_referencing_col.clear()
        self.cbo_referencing_col.addItem("")
        self.cbo_referencing_col.addItems(columns_names)
コード例 #26
0
class GridGeneratorDockWidget(QtWidgets.QDockWidget, FORM_CLASS):

    closingPlugin = pyqtSignal()

    def __init__(self, parent=None):
        """Constructor."""
        super(GridGeneratorDockWidget, self).__init__(parent)
        self.gridAndLabelCreator = GridAndLabelCreator()
        self.setupUi(self)
        self.iface = iface
        self.mapLayerSelection.setFilters(QgsMapLayerProxyModel.PolygonLayer)
        self.mapLayerSelection.layerChanged.connect(self.idSelection.setLayer)
        self.okButton.pressed.connect(self.send_inputs)
        self.resetButton.pressed.connect(self.send_reset)

    def send_inputs(self):

        if (not self.mapLayerSelection.currentLayer()) and (
                self.idSelection.currentField()
        ) and (self.idValue.value()) and (self.utmSpacing.value(
        )) and (self.crossesX.value()) and (self.crossesY.value()) and (
                self.mapScale.value()) and (self.labelFontSize.value()) and (
                    self.fontType.currentFont()
                ) and (self.fontTypeLL.currentFont()) and (
                    self.width_geo.value()) and (self.width_utm.value()) and (
                        self.width_buffer_geo.value()) and (
                            self.width_buffer_utm.value()):
            return

        layer = self.mapLayerSelection.currentLayer()
        id_attr = self.idSelection.currentField()
        id_value = self.idValue.value()
        spacing = self.utmSpacing.value()
        crossX = self.crossesX.value()
        crossY = self.crossesY.value()
        scale = self.mapScale.value()
        geo_grid_color = self.geo_grid_color.color()
        utm_grid_color = self.utm_grid_color.color()
        geo_grid_buffer_color = self.geo_grid_buffer_color.color()
        utm_grid_buffer_color = self.utm_grid_buffer_color.color()
        fontSize = self.labelFontSize.value()
        font = self.fontType.currentFont()
        fontLL = self.fontTypeLL.currentFont()
        llcolor = self.llColor.color()
        linwidth_geo = self.width_geo.value()
        linwidth_utm = self.width_utm.value()
        linwidth_buffer_geo = self.width_buffer_geo.value()
        linwidth_buffer_utm = self.width_buffer_utm.value()
        masks_check = self.maskCheckBox.isChecked()
        if layer == None:
            QMessageBox.information(
                self, u"Aviso",
                u"Nenhuma camada selecionada.\nSelecione uma camada vetorial poligonal para gerar o grid."
            )
            return
        else:
            testFeature = layer.getFeatures('"' + id_attr + '"' + '=' +
                                            str(id_value))
            featureList = [i for i in testFeature]
            if not featureList:
                QMessageBox.critical(
                    None, u"Erro",
                    u"Escolha um valor existente do atributo de identificação")
                return
            else:
                dialogUTMZoneSelection = UTMZoneSelection(
                    self.iface, layer, id_attr, id_value, spacing, crossX,
                    crossY, scale, fontSize, font, fontLL, llcolor,
                    linwidth_geo, linwidth_utm, linwidth_buffer_geo,
                    linwidth_buffer_utm, geo_grid_color, utm_grid_color,
                    geo_grid_buffer_color, utm_grid_buffer_color, masks_check)
                dialogUTMZoneSelection.setDialog()

    def send_reset(self):
        layer = self.mapLayerSelection.currentLayer()
        self.gridAndLabelCreator.reset(layer)

    def closeEvent(self, event):
        self.closingPlugin.emit()
        event.accept()
コード例 #27
0
class PlotItem(LogItem):

    # emitted when the style is updated
    style_updated = pyqtSignal()

    def __init__(self,
                 size=QSizeF(400, 200),
                 render_type=POINT_RENDERER,
                 x_orientation=ORIENTATION_LEFT_TO_RIGHT,
                 y_orientation=ORIENTATION_UPWARD,
                 allow_mouse_translation=False,
                 allow_wheel_zoom=False,
                 symbology=None,
                 parent_item=None):
        """
        Parameters
        ----------
        size: QSize
          Size of the item
        render_type: Literal[POINT_RENDERER, LINE_RENDERER, POLYGON_RENDERER]
          Type of renderer
        x_orientation: Literal[ORIENTATION_LEFT_TO_RIGHT, ORIENTATION_RIGHT_TO_LEFT]
        y_orientation: Literal[ORIENTATION_UPWARD, ORIENTATION_DOWNWARD]
        allow_mouse_translation: bool
          Allow the user to translate the item with the mouse (??)
        allow_wheel_zoom: bool
          Allow the user to zoom with the mouse wheel
        symbology: QDomDocument
          QGIS symbology to use for the renderer
        parent_item: QGraphicsItem
        """
        LogItem.__init__(self, parent_item)

        self.__item_size = size
        self.__data_rect = None
        self.__data = None
        self.__delta = None
        self.__x_orientation = x_orientation
        self.__y_orientation = y_orientation

        # origin point of the graph translation, if any
        self.__translation_orig = None

        self.__render_type = render_type  # type: Literal[POINT_RENDERER, LINE_RENDERER, POLYGON_RENDERER]

        self.__allow_mouse_translation = allow_mouse_translation
        self.__allow_wheel_zoom = allow_wheel_zoom

        self.__layer = None

        self.__default_renderers = [
            QgsFeatureRenderer.defaultRenderer(POINT_RENDERER),
            QgsFeatureRenderer.defaultRenderer(LINE_RENDERER),
            QgsFeatureRenderer.defaultRenderer(POLYGON_RENDERER)
        ]
        symbol = self.__default_renderers[1].symbol()
        symbol.setWidth(1.0)
        symbol = self.__default_renderers[0].symbol()
        symbol.setSize(5.0)
        symbol = self.__default_renderers[2].symbol()
        symbol.symbolLayers()[0].setStrokeWidth(1.0)

        if not symbology:
            self.__renderer = self.__default_renderers[self.__render_type]
        else:
            self.__renderer = QgsFeatureRenderer.load(
                symbology.documentElement(), QgsReadWriteContext())

        # index of the current point to label
        self.__old_point_to_label = None
        self.__point_to_label = None

    def boundingRect(self):
        return QRectF(0, 0, self.__item_size.width(),
                      self.__item_size.height())

    def height(self):
        return self.__item_size.height()

    def set_height(self, height):
        self.__item_size.setHeight(height)

    def width(self):
        return self.__item_size.width()

    def set_width(self, width):
        self.__item_size.setWidth(width)

    def set_data_window(self, window):
        """window: QRectF"""
        self.__data_rect = window

    def min_depth(self):
        if self.__data_rect is None:
            return None
        return self.__data_rect.x()

    def max_depth(self):
        if self.__data_rect is None:
            return None
        return (self.__data_rect.x() + self.__data_rect.width())

    def set_min_depth(self, min_depth):
        if self.__data_rect is not None:
            self.__data_rect.setX(min_depth)

    def set_max_depth(self, max_depth):
        if self.__data_rect is not None:
            w = max_depth - self.__data_rect.x()
            self.__data_rect.setWidth(w)

    def layer(self):
        return self.__layer

    def set_layer(self, layer):
        self.__layer = layer

    def data_window(self):
        return self.__data_rect

    def set_data(self, x_values, y_values):
        self.__x_values = x_values
        self.__y_values = y_values

        if len(self.__x_values) != len(self.__y_values):
            raise ValueError("X and Y array has different length : "
                             "{} != {}".format(len(self.__x_values),
                                               len(self.__y_values)))

        # Remove None values
        for i in reversed(range(len(self.__y_values))):
            if (self.__y_values[i] is None or self.__x_values[i] is None
                    or math.isnan(self.__y_values[i])
                    or math.isnan(self.__x_values[i])):
                self.__y_values.pop(i)
                self.__x_values.pop(i)
        if not self.__x_values:
            return

        # Initialize data rect to display all data
        # with a 20% buffer around Y values
        min_x = min(self.__x_values)
        max_x = max(self.__x_values)
        min_y = min(self.__y_values)
        max_y = max(self.__y_values)
        h = max_y - min_y
        if h == 0.0:
            h = 1.0
        min_y -= h * 0.1
        max_y += h * 0.1
        self.__data_rect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y)

    def renderer(self):
        return self.__renderer

    def paint(self, painter, option, widget):
        self.draw_background(painter)
        if self.__data_rect is None:
            return

        # Look for the first and last X values that fit our rendering rect
        imin_x = bisect.bisect_left(self.__x_values, self.__data_rect.x())
        imax_x = bisect.bisect_right(self.__x_values, self.__data_rect.right())

        # For lines and polygons, retain also one value before the min and one after the max
        # so that lines do not appear truncated
        # Do this only if we have at least one point to render within out rect
        if imin_x > 0 and imin_x < len(
                self.__x_values
        ) and self.__x_values[imin_x] >= self.__data_rect.x():
            # FIXME add a test to avoid adding a point too "far away" ?
            imin_x -= 1
        if imax_x < len(self.__x_values) - 1 and self.__x_values[
                imax_x] <= self.__data_rect.right():
            # FIXME add a test to avoid adding a point too "far away" ?
            imax_x += 1

        x_values_slice = np.array(self.__x_values[imin_x:imax_x])
        y_values_slice = np.array(self.__y_values[imin_x:imax_x])

        if len(x_values_slice) == 0:
            return

        # filter points that are not None (nan in numpy arrays)
        n_points = len(x_values_slice)

        if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD:
            if self.__data_rect.width() > 0:
                rw = float(self.__item_size.width()) / self.__data_rect.width()
            else:
                rw = float(self.__item_size.width())
            if self.__data_rect.height() > 0:
                rh = float(
                    self.__item_size.height()) / self.__data_rect.height()
            else:
                rh = float(self.__item_size.height())
            xx = (x_values_slice - self.__data_rect.x()) * rw
            yy = (y_values_slice - self.__data_rect.y()) * rh
        elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT:
            if self.__data_rect.width() > 0:
                rw = float(
                    self.__item_size.height()) / self.__data_rect.width()
            else:
                rw = float(self.__item_size.height())
            if self.__data_rect.height() > 0:
                rh = float(
                    self.__item_size.width()) / self.__data_rect.height()
            else:
                rh = float(self.__item_size.width())
            xx = (y_values_slice - self.__data_rect.y()) * rh
            yy = self.__item_size.height() - (x_values_slice -
                                              self.__data_rect.x()) * rw

        if self.__render_type == LINE_RENDERER:
            # WKB structure of a linestring
            #
            #   01 : endianness
            #   02 00 00 00 : WKB type (linestring)
            #   nn nn nn nn : number of points (int32)
            # Then, for each point:
            #   xx xx xx xx xx xx xx xx : X coordinate (float64)
            #   yy yy yy yy yy yy yy yy : Y coordinate (float64)

            wkb = np.zeros(8 * 2 * n_points + 9, dtype='uint8')
            wkb[0] = 1  # wkb endianness
            wkb[1] = 2  # linestring
            size_view = np.ndarray(buffer=wkb,
                                   dtype='int32',
                                   offset=5,
                                   shape=(1, ))
            size_view[0] = n_points
            coords_view = np.ndarray(buffer=wkb,
                                     dtype='float64',
                                     offset=9,
                                     shape=(n_points, 2))
            coords_view[:, 0] = xx[:]
            coords_view[:, 1] = yy[:]
        elif self.__render_type == POINT_RENDERER:
            # WKB structure of a multipoint
            #
            #   01 : endianness
            #   04 00 00 00 : WKB type (multipoint)
            #   nn nn nn nn : number of points (int32)
            # Then, for each point:
            #   01 : endianness
            #   01 00 00 00 : WKB type (point)
            #   xx xx xx xx xx xx xx xx : X coordinate (float64)
            #   yy yy yy yy yy yy yy yy : Y coordinate (float64)

            wkb = np.zeros((8 * 2 + 5) * n_points + 9, dtype='uint8')
            wkb[0] = 1  # wkb endianness
            wkb[1] = 4  # multipoint
            size_view = np.ndarray(buffer=wkb,
                                   dtype='int32',
                                   offset=5,
                                   shape=(1, ))
            size_view[0] = n_points
            coords_view = np.ndarray(buffer=wkb,
                                     dtype='float64',
                                     offset=9 + 5,
                                     shape=(n_points, 2),
                                     strides=(16 + 5, 8))
            coords_view[:, 0] = xx[:]
            coords_view[:, 1] = yy[:]
            # header of each point
            h_view = np.ndarray(buffer=wkb,
                                dtype='uint8',
                                offset=9,
                                shape=(n_points, 2),
                                strides=(16 + 5, 1))
            h_view[:, 0] = 1  # endianness
            h_view[:, 1] = 1  # point

        elif self.__render_type == POLYGON_RENDERER:
            # WKB structure of a polygon
            #
            #   01 : endianness
            #   03 00 00 00 : WKB type (polygon)
            #   01 00 00 00 : Number of rings (always 1 here)
            #   nn nn nn nn : number of points (int32)
            # Then, for each point:
            #   xx xx xx xx xx xx xx xx : X coordinate (float64)
            #   yy yy yy yy yy yy yy yy : Y coordinate (float64)
            #
            # We add two additional points to close the polygon

            wkb = np.zeros(8 * 2 * (n_points + 2) + 9 + 4, dtype='uint8')
            wkb[0] = 1  # wkb endianness
            wkb[1] = 3  # polygon
            wkb[5] = 1  # number of rings
            size_view = np.ndarray(buffer=wkb,
                                   dtype='int32',
                                   offset=9,
                                   shape=(1, ))
            size_view[0] = n_points + 2
            coords_view = np.ndarray(buffer=wkb,
                                     dtype='float64',
                                     offset=9 + 4,
                                     shape=(n_points, 2))
            coords_view[:, 0] = xx[:]
            coords_view[:, 1] = yy[:]
            # two extra points
            extra_coords = np.ndarray(buffer=wkb,
                                      dtype='float64',
                                      offset=8 * 2 * n_points + 9 + 4,
                                      shape=(2, 2))
            if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD:
                extra_coords[0, 0] = coords_view[-1, 0]
                extra_coords[0, 1] = 0.0
                extra_coords[1, 0] = coords_view[0, 0]
                extra_coords[1, 1] = 0.0
            elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT:
                extra_coords[0, 0] = 0.0
                extra_coords[0, 1] = coords_view[-1, 1]
                extra_coords[1, 0] = 0.0
                extra_coords[1, 1] = coords_view[0, 1]

        # build a geometry from the WKB
        # since numpy arrays have buffer protocol, sip is able to read it
        geom = QgsGeometry()
        geom.fromWkb(wkb.tobytes())

        painter.setClipRect(0, 0, self.__item_size.width(),
                            self.__item_size.height())

        fields = QgsFields()
        #fields.append(QgsField("", QVariant.String))
        feature = QgsFeature(fields, 1)
        feature.setGeometry(geom)

        context = qgis_render_context(painter, self.__item_size.width(),
                                      self.__item_size.height())
        context.setExtent(
            QgsRectangle(0, 1, self.__item_size.width(),
                         self.__item_size.height()))

        self.__renderer.startRender(context, fields)
        self.__renderer.renderFeature(feature, context)
        self.__renderer.stopRender(context)

        if self.__point_to_label is not None:
            i = self.__point_to_label
            x, y = self.__x_values[i], self.__y_values[i]
            if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD:
                px = (x - self.__data_rect.x()) * rw
                py = self.__item_size.height() - (y -
                                                  self.__data_rect.y()) * rh
            elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT:
                px = (y - self.__data_rect.y()) * rh
                py = (x - self.__data_rect.x()) * rw
            painter.drawLine(px - 5, py, px + 5, py)
            painter.drawLine(px, py - 5, px, py + 5)

    def mouseMoveEvent(self, event):
        if not self.__x_values:
            return
        if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD:
            xx = (event.scenePos().x() - self.pos().x()) / self.width(
            ) * self.__data_rect.width() + self.__data_rect.x()
        elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT:
            xx = (event.scenePos().y() - self.pos().y()) / self.height(
            ) * self.__data_rect.width() + self.__data_rect.x()
        i = bisect.bisect_left(self.__x_values, xx)
        if i >= 0 and i < len(self.__x_values):
            # switch the attached point when we are between two points
            if i > 0 and (xx - self.__x_values[i - 1]) < (self.__x_values[i] -
                                                          xx):
                i -= 1
            self.__point_to_label = i
        else:
            self.__point_to_label = None
        if self.__point_to_label != self.__old_point_to_label:
            self.update()
        if self.__point_to_label is not None:
            x, y = self.__x_values[i], self.__y_values[i]
            if self.__x_orientation == ORIENTATION_LEFT_TO_RIGHT and self.__y_orientation == ORIENTATION_UPWARD:
                dt = datetime.fromtimestamp(x, UTC())
                txt = "Time: {} Value: {}".format(dt.strftime("%x %X"), y)
            elif self.__x_orientation == ORIENTATION_DOWNWARD and self.__y_orientation == ORIENTATION_LEFT_TO_RIGHT:
                txt = "Depth: {} Value: {}".format(x, y)
            self.tooltipRequested.emit(txt)

        self.__old_point_to_label = self.__point_to_label

    def edit_style(self):
        from qgis.gui import QgsSingleSymbolRendererWidget
        from qgis.core import QgsStyle

        style = QgsStyle()
        sw = QStackedWidget()
        sw.addWidget
        for i in range(3):
            if self.__renderer and i == self.__render_type:
                w = QgsSingleSymbolRendererWidget(self.__layer, style,
                                                  self.__renderer)
            else:
                w = QgsSingleSymbolRendererWidget(self.__layer, style,
                                                  self.__default_renderers[i])
            sw.addWidget(w)

        combo = QComboBox()
        combo.addItem("Points")
        combo.addItem("Line")
        combo.addItem("Polygon")

        combo.currentIndexChanged[int].connect(sw.setCurrentIndex)
        combo.setCurrentIndex(self.__render_type)

        dlg = QDialog()

        vbox = QVBoxLayout()

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn.accepted.connect(dlg.accept)
        btn.rejected.connect(dlg.reject)

        vbox.addWidget(combo)
        vbox.addWidget(sw)
        vbox.addWidget(btn)

        dlg.setLayout(vbox)
        dlg.resize(800, 600)

        r = dlg.exec_()

        if r == QDialog.Accepted:
            self.__render_type = combo.currentIndex()
            self.__renderer = sw.currentWidget().renderer().clone()
            self.update()
            self.style_updated.emit()

    def qgis_style(self):
        """Returns the current style, as a QDomDocument"""
        from PyQt5.QtXml import QDomDocument
        from qgis.core import QgsReadWriteContext

        doc = QDomDocument()
        elt = self.__renderer.save(doc, QgsReadWriteContext())
        doc.appendChild(elt)
        return (doc, self.__render_type)
コード例 #28
0
class WidgetWrapper(QObject):

    widgetValueHasChanged = pyqtSignal(object)

    def __init__(self, param, dialog, row=0, col=0, **kwargs):
        QObject.__init__(self)
        self.param = param
        self.dialog = dialog
        self.row = row
        self.col = col
        self.dialogType = dialogTypes.get(dialog.__class__.__name__,
                                          DIALOG_STANDARD)
        self.widget = self.createWidget(**kwargs)
        if param.default is not None:
            self.setValue(param.default)

    def comboValue(self, validator=None, combobox=None):
        if combobox is None:
            combobox = self.widget
        idx = combobox.findText(combobox.currentText())
        if idx < 0:
            v = combobox.currentText().strip()
            if validator is not None and not validator(v):
                raise InvalidParameterValue()
            return v
        return combobox.currentData()

    def createWidget(self, **kwargs):
        pass

    def setValue(self, value):
        pass

    def setComboValue(self, value, combobox=None):
        if combobox is None:
            combobox = self.widget
        if isinstance(value, list):
            value = value[0]
        values = [combobox.itemData(i) for i in range(combobox.count())]
        try:
            idx = values.index(value)
            combobox.setCurrentIndex(idx)
            return
        except ValueError:
            pass
        if combobox.isEditable():
            if value is not None:
                combobox.setEditText(str(value))
        else:
            combobox.setCurrentIndex(0)

    def value(self):
        pass

    def postInitialize(self, wrappers):
        pass

    def refresh(self):
        pass

    def getFileName(self, initial_value=''):
        """Shows a file open dialog"""
        settings = QgsSettings()
        if os.path.isdir(initial_value):
            path = initial_value
        elif os.path.isdir(os.path.dirname(initial_value)):
            path = os.path.dirname(initial_value)
        elif settings.contains('/Processing/LastInputPath'):
            path = str(settings.value('/Processing/LastInputPath'))
        else:
            path = ''

        filename, selected_filter = QFileDialog.getOpenFileName(
            self.widget, self.tr('Select file'), path,
            self.tr('All files (*.*);;') + self.param.getFileFilter())
        if filename:
            settings.setValue('/Processing/LastInputPath',
                              os.path.dirname(str(filename)))
        return filename, selected_filter
コード例 #29
0
class ListMultiSelectWidget(QGroupBox):
    """Widget to show two parallel lists and move elements between the two

    usage from code:
        self.myWidget = ListMultiSelectWidget(title='myTitle')
        self.myLayout.insertWidget(1, self.myWidget)
    usage from designer:
        insert a QGroupBox in your UI file
        optionally give a title to the QGroupBox
        promote it to ListMultiSelectWidget
    """

    selection_changed = pyqtSignal()

    def __init__(self, parent=None, title=None):
        QGroupBox.__init__(self)
        self.setTitle(title)

        self.selected_widget = None
        self.deselected_widget = None
        self._setupUI()

        # connect actions
        self.select_all_btn.clicked.connect(self._select_all)
        self.deselect_all_btn.clicked.connect(self._deselect_all)
        self.select_btn.clicked.connect(self._select)
        self.deselect_btn.clicked.connect(self._deselect)

        self.deselected_widget.itemDoubleClicked.connect(self._select)
        self.selected_widget.itemDoubleClicked.connect(self._deselect)

    def get_selected_items(self):
        """
        :return list with all the selected items text
        """
        return self._get_items(self.selected_widget)

    def get_deselected_items(self):
        """
        :return list with all the deselected items text
        """
        return self._get_items(self.deselected_widget)

    def add_selected_items(self, items):
        """
        :param items list of strings to be added in the selected list
        """
        self._add_items(self.selected_widget, items)

    def add_deselected_items(self, items):
        """
        :param items list of strings to be added in the deselected list
        """
        self._add_items(self.deselected_widget, items)

    def set_selected_items(self, items):
        """
        :param items list of strings to be set as the selected list
        """
        self._set_items(self.selected_widget, items)

    def set_deselected_items(self, items):
        """
        :param items list of strings to be set as the deselected list
        """
        self._set_items(self.deselected_widget, items)

    def clear(self):
        """
        removes all items from selected and deselected
        """
        self.set_selected_items([])
        self.set_deselected_items([])

    def addItem(self, item):
        """
        This is for Processing
        :param item: string to be added in the deselected list
        """
        self.add_deselected_items([item])

    def addItems(self, items):
        """
        This is for Processing
        :param items: list of strings to be added in the deselected list
        """
        self.add_deselected_items(items)

    def _get_items(self, widget):
        for i in range(widget.count()):
            yield widget.item(i).text()

    def _set_items(self, widget, items):
        widget.clear()
        self._add_items(widget, items)

    def _add_items(self, widget, items):
        widget.addItems(items)

    def _select_all(self):
        self.deselected_widget.selectAll()
        self._do_move(self.deselected_widget, self.selected_widget)

    def _deselect_all(self):
        self.selected_widget.selectAll()
        self._do_move(self.selected_widget, self.deselected_widget)

    def _select(self):
        self._do_move(self.deselected_widget, self.selected_widget)

    def _deselect(self):
        self._do_move(self.selected_widget, self.deselected_widget)

    def _do_move(self, fromList, toList):
        for item in fromList.selectedItems():
            prev_from_item = fromList.item(fromList.row(item) - 1)
            toList.addItem(fromList.takeItem(fromList.row(item)))
            fromList.scrollToItem(prev_from_item)
        self.selection_changed.emit()

    def _setupUI(self):
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

        self.setMinimumHeight(180)

        self.main_horizontal_layout = QHBoxLayout(self)

        italic_font = QFont()
        italic_font.setItalic(True)

        # deselected widget
        self.deselected_widget = QListWidget(self)
        self._set_list_widget_defaults(self.deselected_widget)
        deselected_label = QLabel()
        deselected_label.setText('Deselected')
        deselected_label.setAlignment(Qt.AlignCenter)
        deselected_label.setFont(italic_font)
        deselected_v_layout = QVBoxLayout()
        deselected_v_layout.addWidget(deselected_label)
        deselected_v_layout.addWidget(self.deselected_widget)

        # selected widget
        self.selected_widget = QListWidget(self)
        self._set_list_widget_defaults(self.selected_widget)
        selected_label = QLabel()
        selected_label.setText('Selected')
        selected_label.setAlignment(Qt.AlignCenter)
        selected_label.setFont(italic_font)
        selected_v_layout = QVBoxLayout()
        selected_v_layout.addWidget(selected_label)
        selected_v_layout.addWidget(self.selected_widget)

        # buttons
        self.buttons_vertical_layout = QVBoxLayout()
        self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1)

        self.select_all_btn = SmallQPushButton('>>')
        self.deselect_all_btn = SmallQPushButton('<<')
        self.select_btn = SmallQPushButton('>')
        self.deselect_btn = SmallQPushButton('<')
        self.select_btn.setToolTip('Add the selected items')
        self.deselect_btn.setToolTip('Remove the selected items')
        self.select_all_btn.setToolTip('Add all')
        self.deselect_all_btn.setToolTip('Remove all')

        # add buttons
        spacer_label = QLabel()  # pragmatic way to create a spacer with
        # the same height of the labels on top
        # of the lists, in order to align the
        # buttons with the lists.
        self.buttons_vertical_layout.addWidget(spacer_label)
        self.buttons_vertical_layout.addWidget(self.select_btn)
        self.buttons_vertical_layout.addWidget(self.deselect_btn)
        self.buttons_vertical_layout.addWidget(self.select_all_btn)
        self.buttons_vertical_layout.addWidget(self.deselect_all_btn)

        # add sub widgets
        self.main_horizontal_layout.addLayout(deselected_v_layout)
        self.main_horizontal_layout.addLayout(self.buttons_vertical_layout)
        self.main_horizontal_layout.addLayout(selected_v_layout)

    def _set_list_widget_defaults(self, widget):
        widget.setAlternatingRowColors(True)
        widget.setSortingEnabled(True)
        widget.setDragEnabled(True)
        widget.setDragDropMode(QAbstractItemView.DragDrop)
        widget.setDragDropOverwriteMode(False)
        widget.setDefaultDropAction(Qt.MoveAction)
        widget.setSelectionMode(QAbstractItemView.MultiSelection)
コード例 #30
0
ファイル: NumberInputPanel.py プロジェクト: ufolr/QGIS
class NumberInputPanel(NUMBER_BASE, NUMBER_WIDGET):
    """
    Number input panel for use outside the modeler - this input panel
    contains a user friendly spin box for entering values.
    """

    hasChanged = pyqtSignal()

    def __init__(self, param):
        super(NumberInputPanel, self).__init__(None)
        self.setupUi(self)

        self.spnValue.setExpressionsEnabled(True)

        self.param = param
        if self.param.dataType() == QgsProcessingParameterNumber.Integer:
            self.spnValue.setDecimals(0)
        else:
            # Guess reasonable step value
            if self.param.maximum() is not None and self.param.minimum(
            ) is not None:
                try:
                    self.spnValue.setSingleStep(
                        self.calculateStep(float(self.param.minimum()),
                                           float(self.param.maximum())))
                except:
                    pass

        if self.param.maximum() is not None:
            self.spnValue.setMaximum(self.param.maximum())
        else:
            self.spnValue.setMaximum(999999999)
        if self.param.minimum() is not None:
            self.spnValue.setMinimum(self.param.minimum())
        else:
            self.spnValue.setMinimum(-999999999)

        self.allowing_null = False
        # set default value
        if param.flags() & QgsProcessingParameterDefinition.FlagOptional:
            self.spnValue.setShowClearButton(True)
            min = self.spnValue.minimum() - 1
            self.spnValue.setMinimum(min)
            self.spnValue.setValue(min)
            self.spnValue.setSpecialValueText(self.tr('Not set'))
            self.allowing_null = True

        if param.defaultValue() is not None:
            self.setValue(param.defaultValue())
            if not self.allowing_null:
                try:
                    self.spnValue.setClearValue(float(param.defaultValue()))
                except:
                    pass
        elif self.param.minimum() is not None:
            try:
                self.setValue(float(self.param.minimum()))
                if not self.allowing_null:
                    self.spnValue.setClearValue(float(self.param.minimum()))
            except:
                pass
        elif not self.allowing_null:
            self.setValue(0)
            self.spnValue.setClearValue(0)

        # we don't show the expression button outside of modeler
        self.layout().removeWidget(self.btnSelect)
        sip.delete(self.btnSelect)
        self.btnSelect = None

        if not self.param.isDynamic():
            # only show data defined button for dynamic properties
            self.layout().removeWidget(self.btnDataDefined)
            sip.delete(self.btnDataDefined)
            self.btnDataDefined = None
        else:
            self.btnDataDefined.init(0, QgsProperty(),
                                     self.param.dynamicPropertyDefinition())
            self.btnDataDefined.registerEnabledWidget(self.spnValue, False)

        self.spnValue.valueChanged.connect(lambda: self.hasChanged.emit())

    def setDynamicLayer(self, layer):
        try:
            self.btnDataDefined.setVectorLayer(self.getLayerFromValue(layer))
        except:
            pass

    def getLayerFromValue(self, value):
        context = createContext()
        if isinstance(value, QgsProcessingFeatureSourceDefinition):
            value, ok = value.source.valueAsString(context.expressionContext())
        if isinstance(value, str):
            value = QgsProcessingUtils.mapLayerFromString(value, context)
        return value

    def getValue(self):
        if self.btnDataDefined is not None and self.btnDataDefined.isActive():
            return self.btnDataDefined.toProperty()
        elif self.allowing_null and self.spnValue.value(
        ) == self.spnValue.minimum():
            return None
        else:
            return self.spnValue.value()

    def setValue(self, value):
        try:
            self.spnValue.setValue(float(value))
        except:
            return

    def calculateStep(self, minimum, maximum):
        value_range = maximum - minimum
        if value_range <= 1.0:
            step = value_range / 10.0
            # round to 1 significant figrue
            return round(step, -int(math.floor(math.log10(step))))
        else:
            return 1.0