Exemple #1
0
    def runImportObservationData(self):

        # Get the list of series
        series = self.getSeries()
        if not series:
            return

        # Create dialog to let the user choose the serie
        dialog = QDialog()
        dialog.setWindowTitle(tr('Import observation data'))
        layout = QVBoxLayout()
        dialog.setLayout(layout)

        # Combobox
        combo_box = QComboBox()
        combo_box.setObjectName((tr('Choose series')))
        for i, d in enumerate(series):
            combo_box.addItem(d[1])
            combo_box.setItemData(i, d[0])
        layout.addWidget(combo_box)

        # Validation button
        button_box = QDialogButtonBox()
        button_box.addButton(QDialogButtonBox.Ok)
        button_box.button(QDialogButtonBox.Ok).clicked.connect(dialog.accept)
        layout.addWidget(button_box)

        if dialog.exec_() == QDialog.Accepted:
            idx = combo_box.currentIndex()
            val = combo_box.itemData(idx)
            self.openImportObservationData(val)
Exemple #2
0
    def showAdvancedVectorDialog(self):
        dlg = QDialog()
        dlg.setObjectName('dbManagerAdvancedVectorDialog')
        settings = QgsSettings()
        dlg.restoreGeometry(settings.value("/DB_Manager/advancedAddDialog/geometry", QByteArray(), type=QByteArray))
        layout = QFormLayout()
        dlg.setLayout(layout)
        dlg.setWindowTitle(self.tr('Add Layer {}').format(self.name))
        geometryTypeComboBox = QComboBox()
        geometryTypeComboBox.addItem(self.tr('Point'), 'POINT')
        geometryTypeComboBox.addItem(self.tr('Line'), 'LINESTRING')
        geometryTypeComboBox.addItem(self.tr('Polygon'), 'POLYGON')
        layout.addRow(self.tr('Geometry Type'), geometryTypeComboBox)
        zCheckBox = QCheckBox(self.tr('With Z'))
        mCheckBox = QCheckBox(self.tr('With M'))
        layout.addRow(zCheckBox)
        layout.addRow(mCheckBox)
        crsSelector = QgsProjectionSelectionWidget()
        crsSelector.setCrs(self.crs())
        layout.addRow(self.tr('CRS'), crsSelector)

        def selectedGeometryType():
            geomType = geometryTypeComboBox.currentData()
            if zCheckBox.isChecked():
                geomType += 'Z'
            if mCheckBox.isChecked():
                geomType += 'M'

            return geomType

        def selectedCrs():
            return crsSelector.crs()

        addButton = QPushButton(self.tr('Load Layer'))
        addButton.clicked.connect(lambda: self.addLayer(selectedGeometryType(), selectedCrs()))
        btns = QDialogButtonBox(QDialogButtonBox.Cancel)
        btns.addButton(addButton, QDialogButtonBox.ActionRole)

        layout.addRow(btns)

        addButton.clicked.connect(dlg.accept)
        btns.accepted.connect(dlg.accept)
        btns.rejected.connect(dlg.reject)

        dlg.exec_()

        settings = QgsSettings()
        settings.setValue("/DB_Manager/advancedAddDialog/geometry", dlg.saveGeometry())
class ErrorReportDialog(QDialog):
    def __init__(self, title, message, errors, username, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(title)

        self.verticalLayout = QVBoxLayout(self)

        self.label = QLabel(message, self)
        self.verticalLayout.addWidget(self.label)

        self.plainTextEdit = QPlainTextEdit(self)
        self.plainTextEdit.setPlainText(errors)
        self.plainTextEdit.setReadOnly(True)
        self.verticalLayout.addWidget(self.plainTextEdit)

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
        self.verticalLayout.addWidget(self.buttonBox)
        self.reportButton = self.buttonBox.addButton(self.tr("Report error"), QDialogButtonBox.ActionRole)

        self.reportButton.clicked.connect(self.__reportError)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.username = username
        self.metadata = MetaData()

    def __reportError(self):
        body = ("Please provide any additional information here:\n\n\n"
                "If you encountered an upload error, if possible please attach a zip file containing a minimal extract of the dataset, as well as the QGIS project file.\n\n\n"
                "Technical information:\n%s\n\n"
                "Versions:\n"
                " QGIS: %s\n"
                " Python: %s\n"
                " OS: %s\n"
                " QGIS Cloud Plugin: %s\n\n"
                "Username: %s\n") % (
                    self.plainTextEdit.toPlainText(),
                    Qgis.QGIS_VERSION,
                    sys.version.replace("\n", " "),
                    platform.platform(),
                    self.metadata.version(),
                    self.username)
        url = QUrl()
        url.toEncoded("mailto:[email protected]?subject=%s&body=%s" % (
                urllib.parse.quote(self.windowTitle()),
                urllib.parse.quote(body)),
        )
        QDesktopServices.openUrl(url)
Exemple #4
0
class ImportDialog(QDialog):

    def __init__(self, parent, repo = None, layer = None):
        super(ImportDialog, self).__init__(parent)
        self.repo = repo
        self.layer = layer
        self.ok = False
        self.initGui()

    def initGui(self):
        self.setWindowTitle('Add layer to GeoGig repository')
        verticalLayout = QVBoxLayout()

        if self.repo is None:
            repos = repository.repos
            layerLabel = QLabel('Repository')
            verticalLayout.addWidget(layerLabel)
            self.repoCombo = QComboBox()
            self.repoCombo.addItems(["%s - %s" % (r.group, r.title) for r in repos])
            self.repoCombo.currentIndexChanged.connect(self.updateBranches)
            verticalLayout.addWidget(self.repoCombo)
        if self.layer is None:
            layerLabel = QLabel('Layer')
            verticalLayout.addWidget(layerLabel)
            self.layerCombo = QComboBox()
            layerNames = [layer.name() for layer in getVectorLayers()
                          if layer.source().lower().split("|")[0].split(".")[-1] in["gpkg", "geopkg"]
                          and not isRepoLayer(layer)]
            self.layerCombo.addItems(layerNames)
            verticalLayout.addWidget(self.layerCombo)

        self.branchLabel = QLabel("Branch")
        verticalLayout.addWidget(self.branchLabel)

        self.branchCombo = QComboBox()
        self.branches = self.repo.branches() if self.repo is not None else repos[0].branches()
        self.branchCombo.addItems(self.branches)
        verticalLayout.addWidget(self.branchCombo)

        messageLabel = QLabel('Message to describe this update')
        verticalLayout.addWidget(messageLabel)

        self.messageBox = QPlainTextEdit()
        self.messageBox.textChanged.connect(self.messageHasChanged)
        verticalLayout.addWidget(self.messageBox)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel)
        self.importButton = QPushButton("Add layer")
        self.importButton.clicked.connect(self.importClicked)
        self.importButton.setEnabled(False)
        self.buttonBox.addButton(self.importButton, QDialogButtonBox.ApplyRole)
        self.buttonBox.rejected.connect(self.cancelPressed)
        verticalLayout.addWidget(self.buttonBox)

        self.setLayout(verticalLayout)

        self.resize(600, 300)

        self.messageBox.setFocus()

    def updateBranches(self):
        self.branchCombo.clear()
        repo = repository.repos[self.repoCombo.currentIndex()]
        self.branches = repo.branches()
        self.branchCombo.addItems(self.branches)

    def messageHasChanged(self):
        self.importButton.setEnabled(self.messageBox.toPlainText() != "")


    def importClicked(self):
        if self.repo is None:
            self.repo = repository.repos[self.repoCombo.currentIndex()]
        if self.layer is None:
            text = self.layerCombo.currentText()
            self.layer = resolveLayer(text)

        user, email = config.getUserInfo()
        if user is None:
            self.close()
            return
        message = self.messageBox.toPlainText()

        branch = self.branchCombo.currentText()
        try:
            self.repo.importgeopkg(self.layer, branch, message, user, email, False)
            filename, layername = namesFromLayer(self.layer)
            self.repo.checkoutlayer(filename, layername, ref = branch)
            self.layer.reload()
            self.layer.triggerRepaint()
        except GeoGigException as e:
            iface.messageBar().pushMessage("Error", str(e),
                                           level=QgsMessageBar.CRITICAL,
                                           duration=5)
            self.close()
            return

        addTrackedLayer(self.layer, self.repo.url)

        self.ok = True
        iface.messageBar().pushMessage("Layer was correctly added to repository",
                                       level=QgsMessageBar.INFO,
                                       duration=5)
        self.close()


    def cancelPressed(self):
        self.close()
Exemple #5
0
class GeometryEditor(QDialog):
    def __init__(self, parent, profile, table, args=None):
        QDialog.__init__(self, parent)

        self.profile = profile
        self.tableName = table
        self.geom_data = args
        self.oldText = self.geom_data[0]
        # add control to the dialog
        self.label = QLabel()
        self.labelName = QLabel()

        self.label.setText(
            QApplication.translate("GeometryEditor", "Select Geometry Type"))
        self.comboField = QComboBox()
        self.labelName.setText(
            QApplication.translate("GeometryEditor", "Edit Type Name"))
        self.textFieldName = QLineEdit()
        self.sridButton = QPushButton()
        self.sridButton.setText(
            QApplication.translate("GeometryEditor",
                                   "Select Coordinate System "))
        self.textField = QLineEdit()

        setCollectiontypes(geometry_collections, self.comboField)

        self.buttons = QDialogButtonBox()
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.sridButton.clicked.connect(self.projectionsSettings)

        layout = QGridLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.comboField)
        layout.addWidget(self.labelName)
        layout.addWidget(self.textFieldName)
        layout.addWidget(self.sridButton)
        layout.addWidget(self.textField)
        layout.addWidget(self.buttons)
        self.setLayout(layout)
        self.setWindowTitle(
            QApplication.translate("GeometryEditor",
                                   "Geometry Column Property"))

        self.on_edit_session()
        self.buttons.accepted.connect(self.setGeometrySetting)
        self.buttons.rejected.connect(self.cancel)

    def projectionsSettings(self):
        '''let user select the projections for the data'''
        projSelect = ProjectionSelector(self)
        projection = projSelect.loadAvailableSystems()
        self.textField.setText(str(projection))

    def on_edit_session(self):
        self.textFieldName.setText(self.geom_data[0])
        self.textField.setText(self.geom_data[2])
        index = self.comboField.findData(self.geom_data[1], Qt.UserRole)
        if index:
            self.comboField.setCurrentIndex(index)
        else:
            self.comboField.setCurrentIndex(0)

    def setGeometrySetting(self):
        if self.textField.text() == '':
            self.ErrorInfoMessage(
                QApplication.translate("GeometryEditor",
                                       "Projections is not selected"))
            return

        self.value = self.textField.text()[5:]
        if not self.value:
            self.value = self.textField.text()
        geomType = UserData(self.comboField)
        edit_geom_column(self.profile, self.tableName, 'column',
                         str(self.oldText), str(self.textFieldName.text()),
                         str(geomType), str(self.value))
        QMessageBox.information(
            None, QApplication.translate("GeometryEditor", "Saving geometry"),
            QApplication.translate("GeometryEditor",
                                   "New data Saved successfully"))
        self.accept()

    def cancel(self):
        self.close()

    def ErrorInfoMessage(self, Message):
        # Error Message Box
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowTitle(
            QApplication.translate("GeometryEditor", "Geometry Settings"))
        msg.setText(Message)
        msg.exec_()
class ImportDialog(QDialog):

    def __init__(self, parent, repo = None, layer = None):
        super(ImportDialog, self).__init__(parent)
        self.setObjectName("ImportDialog")
        self.repo = repo
        self.layer = layer
        self.ok = False
        self.initGui()

    def initGui(self):
        self.setWindowTitle('Import to GeoGig')
        verticalLayout = QVBoxLayout()

        if self.repo is None:
            repos = repository.repos
            layerLabel = QLabel('Repository')
            verticalLayout.addWidget(layerLabel)
            self.repoCombo = QComboBox()
            self.repoCombo.addItems(["%s - %s" % (r.group, r.title) for r in repos])
            self.repoCombo.currentIndexChanged.connect(self.updateBranches)
            verticalLayout.addWidget(self.repoCombo)
        if self.layer is None:
            layerLabel = QLabel('Layer')
            verticalLayout.addWidget(layerLabel)
            self.layerCombo = QComboBox()
            layerNames = [layer.name() for layer in vectorLayers()
                          if layer.source().lower().split("|")[0].split(".")[-1] in["gpkg", "geopkg"]
                          and not isRepoLayer(layer)]
            self.layerCombo.addItems(layerNames)
            verticalLayout.addWidget(self.layerCombo)

        self.branchLabel = QLabel("Branch")
        verticalLayout.addWidget(self.branchLabel)

        self.branchCombo = QComboBox()
        self.branches = self.repo.branches() if self.repo is not None else repos[0].branches()
        self.branchCombo.addItems(self.branches)
        verticalLayout.addWidget(self.branchCombo)

        messageLabel = QLabel('Message to describe this update')
        verticalLayout.addWidget(messageLabel)

        self.messageBox = QPlainTextEdit()
        verticalLayout.addWidget(self.messageBox)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel)
        self.importButton = QPushButton("Add layer")
        self.importButton.clicked.connect(self.importClicked)
        self.buttonBox.addButton(self.importButton, QDialogButtonBox.ApplyRole)
        self.buttonBox.rejected.connect(self.cancelPressed)
        verticalLayout.addWidget(self.buttonBox)

        self.setLayout(verticalLayout)

        self.resize(600, 300)

        self.messageBox.setFocus()

    def updateBranches(self):
        self.branchCombo.clear()
        repo = repository.repos[self.repoCombo.currentIndex()]
        self.branches = repo.branches()
        self.branchCombo.addItems(self.branches)

    def importClicked(self):

        ret = QMessageBox.warning(config.iface.mainWindow(), 'Import warning',
                "Importing a layer will modify the original layer and might cause data loss.\n"
                "Make sure you have a backup copy of your layer before importing.\n"
                "Do you want to import the selected layer?",
                QMessageBox.Yes | QMessageBox.No)
        if ret == QMessageBox.No:
            return


        if self.repo is None:
            self.repo = repository.repos[self.repoCombo.currentIndex()]
        if self.layer is None:
            text = self.layerCombo.currentText()
            self.layer = layerFromName(text)

        user, email = config.getUserInfo()
        if user is None:
            self.close()
            return
        message = self.messageBox.toPlainText() or datetime.now().strftime("%Y-%m-%d %H_%M_%S")
        branch = self.branchCombo.currentText()
        try:
            self.repo.importgeopkg(self.layer, branch, message, user, email, False)
            filename, layername = namesFromLayer(self.layer)
            self.repo.checkoutlayer(filename, layername, ref = branch)
            self.layer.reload()
            self.layer.triggerRepaint()
        except GeoGigException as e:
            iface.messageBar().pushMessage("Error", str(e),
                                           level=QgsMessageBar.CRITICAL,
                                           duration=5)
            self.close()
            return

        addTrackedLayer(self.layer, self.repo.url)

        self.ok = True
        iface.messageBar().pushMessage("Layer was correctly added to repository",
                                       level=QgsMessageBar.INFO,
                                       duration=5)
        self.close()


    def cancelPressed(self):
        self.close()
Exemple #7
0
class GeometryProperty(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self, parent)

        # add control to the dialog
        self.label = QLabel()
        self.label.setText(
            QApplication.translate("GeometryProperty", "Select Geometry Type"))
        self.comboField = QComboBox()
        self.sridButton = QPushButton()
        self.sridButton.setText(
            QApplication.translate("GeometryProperty",
                                   "Select Coordinate System "))
        self.textField = QLineEdit()

        setCollectiontypes(geometry_collections, self.comboField)

        self.buttons = QDialogButtonBox()
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.sridButton.clicked.connect(self.projectionsSettings)

        layout = QGridLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.comboField)
        layout.addWidget(self.sridButton)
        layout.addWidget(self.textField)
        layout.addWidget(self.buttons)
        self.setLayout(layout)
        self.setWindowTitle(
            QApplication.translate("GeometryProperty",
                                   "Geometry Column Property"))

        self.buttons.accepted.connect(self.setGeometrySetting)
        self.buttons.rejected.connect(self.cancel)

    def projectionsSettings(self):
        '''let user select the projections for the data'''
        projSelect = ProjectionSelector(self)
        projection = projSelect.loadAvailableSystems()
        self.textField.setText(str(projection))

    def setGeometrySetting(self):
        if self.textField.text() == '':
            self.ErrorInfoMessage(
                QApplication.translate("GeometryProperty",
                                       "Projections is not selected"))
            return

        self.value = self.textField.text()[5:]
        geomType = UserData(self.comboField)
        self.geomCollection = [self.value, geomType]
        self.accept()

    def cancel(self):
        self.close()

    def ErrorInfoMessage(self, Message):
        # Error Message Box
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowTitle(
            QApplication.translate("GeometryProperty", "Geometry Settings"))
        msg.setText(Message)
        msg.exec_()
Exemple #8
0
class QvFormSimbMapificacio(QvFormBaseMapificacio):
    def __init__(self, llegenda, capa=None, amplada=500):
        super().__init__(llegenda, amplada)
        if capa is None:
            self.capa = llegenda.currentLayer()
        else:
            self.capa = capa
        self.info = None
        if not self.iniParams():
            return

        self.setWindowTitle('Modificar mapa simbòlic ' + self.renderParams.tipusMapa.lower())

        self.layout = QVBoxLayout()
        self.layout.setSpacing(14)
        self.setLayout(self.layout)

        self.color = QComboBox(self)
        self.color.setEditable(False)
        self.comboColors(self.color)

        self.contorn = QComboBox(self)
        self.contorn.setEditable(False)
        self.comboColors(self.contorn, mv.MAP_CONTORNS)

        self.color.currentIndexChanged.connect(self.canviaContorns)

        self.metode = QComboBox(self)
        self.metode.setEditable(False)
        if self.renderParams.numCategories > 1:
            self.metode.addItems(mv.MAP_METODES_MODIF.keys())
        else:
            self.metode.addItems(mv.MAP_METODES.keys())
        self.metode.setCurrentIndex(-1)
        self.metode.currentIndexChanged.connect(self.canviaMetode)

        self.nomIntervals = QLabel("Nombre d'intervals:", self)
        self.intervals = QSpinBox(self)
        self.intervals.setMinimum(min(2, self.renderParams.numCategories))
        self.intervals.setMaximum(max(mv.MAP_MAX_CATEGORIES, self.renderParams.numCategories))
        self.intervals.setSingleStep(1)
        self.intervals.setValue(4)
        if self.renderParams.tipusMapa == 'Àrees':
            self.intervals.setSuffix("  (depèn del mètode)")
        # self.intervals.valueChanged.connect(self.deselectValue)

        self.nomTamany = QLabel("Tamany cercle:", self)
        self.tamany = QSpinBox(self)
        self.tamany.setMinimum(1)
        self.tamany.setMaximum(12)
        self.tamany.setSingleStep(1)
        self.tamany.setValue(4)

        self.bInfo = QPushButton('Info')
        self.bInfo.clicked.connect(self.veureInfo)

        self.buttons = QDialogButtonBox()
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.buttons.accepted.connect(self.accept)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.buttons.rejected.connect(self.cancel)
        self.buttons.addButton(self.bInfo, QDialogButtonBox.ResetRole)

        self.gSimb = QGroupBox('Simbologia del mapa')
        self.lSimb = QFormLayout()
        self.lSimb.setSpacing(14)
        self.gSimb.setLayout(self.lSimb)

        self.lSimb.addRow('Color base:', self.color)
        self.lSimb.addRow('Color contorn:', self.contorn)
        if self.renderParams.tipusMapa == 'Àrees':
            self.lSimb.addRow('Mètode classificació:', self.metode)
            self.lSimb.addRow(self.nomIntervals, self.intervals)
            self.nomTamany.setVisible(False)
            self.tamany.setVisible(False)
        else:
            self.metode.setVisible(False)
            self.nomIntervals.setVisible(False)
            self.intervals.setVisible(False)
            self.lSimb.addRow(self.nomTamany, self.tamany)

        self.wInterval = []
        for w in self.iniIntervals():
            self.wInterval.append(w)
        self.gInter = self.grupIntervals()

        self.layout.addWidget(self.gSimb)
        if self.renderParams.tipusMapa == 'Àrees':
            self.layout.addWidget(self.gInter)
        self.layout.addWidget(self.buttons)

        self.valorsInicials()

    def iniParams(self):
        self.info = QgsExpressionContextUtils.layerScope(self.capa).variable(mv.MAP_ID)
        if self.info is None:
            return False
        self.renderParams = QvMapRendererParams.fromLayer(self.capa)
        if self.renderParams.msgError == '':
            self.custom = (self.renderParams.modeCategories == 'Personalitzat')
            return True
        else:
            self.msgInfo("No s'han pogut recuperar els paràmetres del mapa simbòlic\n\n" +
                         "Error: " + self.renderParams.msgError)
            return False

    @pyqtSlot()
    def veureInfo(self):
        if self.info is not None:
            box = QMessageBox(self)
            box.setWindowTitle('Info del mapa simbòlic')
            txt = '<table width="600">'
            params = self.info.split('\n')
            for param in params:
                linea = param.strip()
                if linea.endswith(':'):
                    linea += ' ---'
                txt += '<tr><td><nobr>&middot;&nbsp;{}</nobr></td></tr>'.format(linea)
            txt += '</table>'
            box.setTextFormat(Qt.RichText)
            box.setText("Paràmetres d'agregació de dades:")
            box.setInformativeText(txt)
            box.setIcon(QMessageBox.Information)
            box.setStandardButtons(QMessageBox.Ok)
            box.setDefaultButton(QMessageBox.Ok)
            box.exec()

    def valorsInicials(self):
        self.color.setCurrentIndex(self.color.findText(self.renderParams.colorBase))
        self.contorn.setCurrentIndex(self.contorn.findText(self.renderParams.colorContorn))
        self.intervals.setValue(self.renderParams.numCategories)
        self.tamany.setValue(self.renderParams.increase)
        self.metode.setCurrentIndex(self.metode.findText(self.renderParams.modeCategories))

    def valorsFinals(self):
        self.renderParams.colorBase = self.color.currentText()
        self.renderParams.colorContorn = self.contorn.currentText()
        self.renderParams.modeCategories = self.metode.currentText()
        self.renderParams.numCategories = self.intervals.value()
        self.renderParams.increase = self.tamany.value()
        if self.custom:
            self.renderParams.rangsCategories = []
            for fila in self.wInterval:
                self.renderParams.rangsCategories.append((fila[0].text(), fila[2].text()))
            self.renderParams.numCategories = len(self.renderParams.rangsCategories)

    def txtRang(self, num):
        if type(num) == str:
            return num
        return QvApp().locale.toString(num, 'f', self.renderParams.numDecimals)

    def iniFilaInterval(self, iniValor, finValor):
        maxSizeB = 27
        # validator = QDoubleValidator(self)
        # validator.setLocale(QvApp().locale)
        # validator.setNotation(QDoubleValidator.StandardNotation)
        # validator.setDecimals(5)
        validator = QvVerifNumero(self)
        ini = QLineEdit(self)
        ini.setText(self.txtRang(iniValor))
        ini.setValidator(validator)
        sep = QLabel('-', self)
        fin = QLineEdit(self)
        fin.setText(self.txtRang(finValor))
        fin.setValidator(validator)
        fin.editingFinished.connect(self.nouTall)
        add = QPushButton('+', self)
        add.setMaximumSize(maxSizeB, maxSizeB)
        add.setToolTip('Afegeix nou interval')
        add.clicked.connect(self.afegirFila)
        add.setFocusPolicy(Qt.NoFocus)
        rem = QPushButton('-', self)
        rem.setMaximumSize(maxSizeB, maxSizeB)
        rem.setToolTip('Esborra interval')
        rem.clicked.connect(self.eliminarFila)
        rem.setFocusPolicy(Qt.NoFocus)
        return [ini, sep, fin, add, rem]

    def iniIntervals(self):
        for cat in self.renderParams.rangsCategories:
            yield self.iniFilaInterval(cat.lowerValue(), cat.upperValue())

    def grupIntervals(self):
        group = QGroupBox('Definició dels intervals')
        # group.setMinimumWidth(400)
        layout = QGridLayout()
        layout.setSpacing(10)
        # layout.setColumnMinimumWidth(4, 40)
        numFilas = len(self.wInterval)
        for fila, widgets in enumerate(self.wInterval):
            for col, w in enumerate(widgets):
                # Primera fila: solo +
                if fila == 0 and col > 3:
                    w.setVisible(False)
                # # Ultima fila: no hay + ni -
                elif fila > 0 and fila == (numFilas - 1) and col > 2:
                    w.setVisible(False)
                else:
                    w.setVisible(True)
                # Valor inicial deshabilitado (menos 1a fila)
                if col == 0 and fila != 0:
                    w.setDisabled(True)
                w.setProperty('Fila', fila)
                layout.addWidget(w, fila, col)
        group.setLayout(layout)
        return group

    def actGrupIntervals(self):
        self.intervals.setValue(len(self.wInterval))

        self.setUpdatesEnabled(False)
        self.buttons.setVisible(False)
        self.gInter.setVisible(False)

        self.layout.removeWidget(self.buttons)
        self.layout.removeWidget(self.gInter)

        self.gInter.deleteLater()
        self.gInter = self.grupIntervals()

        self.layout.addWidget(self.gInter)
        self.layout.addWidget(self.buttons)

        self.gInter.setVisible(True)
        self.buttons.setVisible(True)

        self.adjustSize()
        self.setUpdatesEnabled(True)

    @pyqtSlot()
    def afegirFila(self):
        masFilas = (len(self.wInterval) < mv.MAP_MAX_CATEGORIES)
        if masFilas:
            f = self.sender().property('Fila') + 1
            ini = self.wInterval[f][0]
            val = ini.text()
            ini.setText('')
            w = self.iniFilaInterval(val, '')
            self.wInterval.insert(f, w)
            self.actGrupIntervals()
            self.wInterval[f][2].setFocus()
        else:
            self.msgInfo("S'ha arribat al màxim d'intervals possibles")

    @pyqtSlot()
    def eliminarFila(self):
        f = self.sender().property('Fila')
        ini = self.wInterval[f][0]
        val = ini.text()
        del self.wInterval[f]
        ini = self.wInterval[f][0]
        ini.setText(val)
        self.actGrupIntervals()

    @pyqtSlot()
    def nouTall(self):
        w = self.sender()
        if w.isModified():
            f = w.property('Fila') + 1
            if f < len(self.wInterval):
                ini = self.wInterval[f][0]
                ini.setText(w.text())
            w.setModified(False)

    @pyqtSlot()
    def canviaMetode(self):
        self.custom = (self.metode.currentText() == 'Personalitzat')
        if self.custom:
            self.intervals.setValue(len(self.wInterval))
        self.intervals.setEnabled(not self.custom)
        self.gInter.setVisible(self.custom)
        self.adjustSize()
        # print('GSIMB -> Ancho:', self.gSimb.size().width(), '- Alto:', self.gSimb.size().height())
        # print('FORM -> Ancho:', self.size().width(), '- Alto:', self.size().height())

    @pyqtSlot()
    def canviaContorns(self):
        self.comboColors(self.contorn, mv.MAP_CONTORNS,
                         mv.MAP_COLORS[self.color.currentText()], True)

    def leSelectFocus(self, wLineEdit):
        lon = len(wLineEdit.text())
        if lon > 0:
            wLineEdit.setSelection(0, lon)
        wLineEdit.setFocus()

    def validaNum(self, wLineEdit):
        val = wLineEdit.validator()
        if val is None:
            return True
        res = val.validate(wLineEdit.text(), 0)
        if res[0] == QValidator.Acceptable:
            return True
        else:
            self.msgInfo("Cal introduir un nombre enter o amb decimals.\n"
                         "Es farà servir la coma (,) per separar els decimals.\n"
                         "I pels milers, opcionalment, el punt (.)")
            self.leSelectFocus(wLineEdit)
            return False

    def validaInterval(self, wLineEdit1, wLineEdit2):
        num1, _ = QvApp().locale.toFloat(wLineEdit1.text())
        num2, _ = QvApp().locale.toFloat(wLineEdit2.text())
        if num2 >= num1:
            return True
        else:
            self.msgInfo("El segon nombre de l'interval ha de ser major que el primer")
            self.leSelectFocus(wLineEdit2)
            return False

    def validaFila(self, fila):
        wLineEdit1 = fila[0]
        wLineEdit2 = fila[2]
        if not self.validaNum(wLineEdit1):
            return False
        if not self.validaNum(wLineEdit2):
            return False
        if not self.validaInterval(wLineEdit1, wLineEdit2):
            return False
        return True

    def valida(self):
        if self.custom:
            for fila in self.wInterval:
                if not self.validaFila(fila):
                    return False
        return True

    def procesa(self):
        self.valorsFinals()
        try:
            mapRenderer = self.renderParams.mapRenderer(self.llegenda)
            if self.custom:
                self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase]
                self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn]
                self.renderer = mapRenderer.customRender(self.capa)
            else:
                self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase]
                if self.renderParams.colorContorn == 'Base':
                    self.renderParams.colorContorn = self.renderParams.colorBase
                else:
                    self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn]
                self.renderParams.modeCategories = \
                    mv.MAP_METODES_MODIF[self.renderParams.modeCategories]
                self.renderer = mapRenderer.calcRender(self.capa)
            if self.renderer is None:
                return "No s'ha pogut elaborar el mapa simbòlic"
            err = self.llegenda.saveStyleToGeoPackage(self.capa, mv.MAP_ID)
            if err != '':
                return "Hi ha hagut problemes al desar la simbologia\n({})".format(err)
            # self.llegenda.modificacioProjecte('mapModified')
            return ''
        except Exception as e:
            return "No s'ha pogut modificar el mapa simbòlic\n({})".format(str(e))
Exemple #9
0
class QvFormNovaMapificacio(QvFormBaseMapificacio):
    def __init__(self, llegenda, amplada=500, mapificacio=None, simple=True):
        super().__init__(llegenda, amplada)

        self.fCSV = mapificacio
        self.simple = simple
        self.taulaMostra = None

        self.setWindowTitle('Afegir capa amb mapa simbòlic')

        self.layout = QVBoxLayout()
        self.layout.setSpacing(14)
        self.setLayout(self.layout)

        if self.fCSV is None:
            self.arxiu = QgsFileWidget()
            self.arxiu.setStorageMode(QgsFileWidget.GetFile)
            self.arxiu.setDialogTitle('Selecciona fitxer de dades…')
            self.arxiu.setDefaultRoot(RUTA_LOCAL)
            self.arxiu.setFilter('Arxius CSV (*.csv)')
            self.arxiu.setSelectedFilter('Arxius CSV (*.csv)')
            self.arxiu.lineEdit().setReadOnly(True)
            self.arxiu.fileChanged.connect(self.arxiuSeleccionat)

        self.zona = QComboBox(self)
        self.zona.setEditable(False)
        self.zona.addItem('Selecciona zona…')
        self.zona.currentIndexChanged.connect(self.canviaZona)

        self.mapa = QComboBox(self)
        self.mapa.setEditable(False)
        self.mapa.setIconSize(QSize(126, 126))
        self.mapa.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.mapa.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Àrees.PNG')), 'Àrees')
        self.mapa.addItem(QIcon(os.path.join(imatgesDir, 'Cercles.PNG')), 'Cercles')

        self.capa = QLineEdit(self)
        self.capa.setMaxLength(40)

        self.tipus = QComboBox(self)
        self.tipus.setEditable(False)
        self.tipus.addItem('Selecciona tipus…')
        self.tipus.addItems(mv.MAP_AGREGACIO.keys())
        self.tipus.currentIndexChanged.connect(self.canviaTipus)

        self.distribucio = QComboBox(self)
        self.distribucio.setEditable(False)
        self.distribucio.addItem(next(iter(mv.MAP_DISTRIBUCIO.keys())))

        self.calcul = QvComboBoxCamps(self)
        self.filtre = QvComboBoxCamps(self, multiple=True)

        self.color = QComboBox(self)
        self.color.setEditable(False)
        self.comboColors(self.color)

        self.metode = QComboBox(self)
        self.metode.setEditable(False)
        self.metode.addItems(mv.MAP_METODES.keys())

        self.intervals = QSpinBox(self)
        self.intervals.setMinimum(2)
        self.intervals.setMaximum(mv.MAP_MAX_CATEGORIES)
        self.intervals.setSingleStep(1)
        self.intervals.setValue(4)
        self.intervals.setSuffix("  (depèn del mètode)")
        # self.intervals.valueChanged.connect(self.deselectValue)

        self.bTaula = QPushButton('Veure arxiu')
        self.bTaula.setEnabled(False)
        self.bTaula.clicked.connect(self.veureArxiu)

        self.buttons = QDialogButtonBox()
        self.buttons.addButton(QDialogButtonBox.Ok)
        self.buttons.accepted.connect(self.accept)
        self.buttons.addButton(QDialogButtonBox.Cancel)
        self.buttons.rejected.connect(self.cancel)
        self.buttons.addButton(self.bTaula, QDialogButtonBox.ResetRole)

        self.gDades = QGroupBox('Agregació de dades')
        self.lDades = QFormLayout()
        self.lDades.setSpacing(14)
        self.gDades.setLayout(self.lDades)

        if self.fCSV is None:
            self.lDades.addRow('Arxiu de dades:', self.arxiu)
        self.lDades.addRow('Zona:', self.zona)
        self.lDades.addRow("Tipus d'agregació:", self.tipus)
        self.lDades.addRow('Camp de càlcul:', self.calcul)
        if self.simple:
            self.filtre.setVisible(False)
            self.distribucio.setVisible(False)
        else:
            self.lDades.addRow('Filtre:', self.filtre)
            self.lDades.addRow('Distribució:', self.distribucio)

        self.gMapa = QGroupBox('Definició del mapa simbòlic')
        self.lMapa = QFormLayout()
        self.lMapa.setSpacing(14)
        self.gMapa.setLayout(self.lMapa)

        self.lMapa.addRow('Nom de capa:', self.capa)
        self.lMapa.addRow('Tipus de mapa:', self.mapa)

        self.gSimb = QGroupBox('Simbologia del mapa')
        self.lSimb = QFormLayout()
        self.lSimb.setSpacing(14)
        self.gSimb.setLayout(self.lSimb)

        self.lSimb.addRow('Color base:', self.color)
        self.lSimb.addRow('Mètode classificació:', self.metode)
        self.lSimb.addRow("Nombre d'intervals:", self.intervals)

        self.layout.addWidget(self.gDades)
        self.layout.addWidget(self.gMapa)
        if self.simple:
            self.gSimb.setVisible(False)
        else:
            self.layout.addWidget(self.gSimb)
        self.layout.addWidget(self.buttons)

        self.adjustSize()

        self.nouArxiu()

    def exec(self):
        # La mapificación solo funciona si está instalado el módulo pandas
        if PANDAS_ENABLED:
            return super().exec()
        else:
            self.msgError(PANDAS_ERROR)
            return QDialog.Rejected

    @pyqtSlot()
    def veureArxiu(self):
        if self.taulaMostra is not None:
            self.taulaMostra.show()
            self.taulaMostra.activateWindow()

    def campsDB(self, nom):
        res = []
        if nom != '':
            fich = RUTA_DADES + mv.MAP_ZONES_DB
            if os.path.isfile(fich):
                conn = sqlite3.connect('file:' + fich + '?mode=ro', uri=True)
                conn.row_factory = sqlite3.Row
                c = conn.cursor()
                c.execute('select * from ' + nom)   # nom.split('.')[0])
                row = c.fetchone()
                # res = [i[0].upper() for i in c.description]
                res = [i.upper() for i in row.keys()]
                conn.close()
        return res

    def soloPrimerItem(self, combo):
        combo.setCurrentIndex(0)
        ultimo = combo.count() - 1
        for n in range(ultimo, 0, -1):
            combo.removeItem(n)

    @pyqtSlot()
    def canviaZona(self):
        self.distribucio.setCurrentIndex(0)
        self.soloPrimerItem(self.distribucio)
        if self.zona.currentIndex() > 0:
            z = self.zona.currentText()
            campsZona = self.campsDB(mv.MAP_ZONES[z][1])
            # Carga combo con distribuciones si el campo correspondiente está en la BBDD
            for dist, campo in mv.MAP_DISTRIBUCIO.items():
                if campo != '' and campo in campsZona:
                    self.distribucio.addItem(dist)

    @pyqtSlot()
    def canviaTipus(self):
        if self.tipus.currentText() == 'Recompte':
            self.calcul.setCurrentIndex(-1)
            self.calcul.setEnabled(False)
        else:
            self.calcul.setEnabled(True)

    def borrarArxiu(self):
        if self.taulaMostra is not None:
            self.taulaMostra.hide()
            self.taulaMostra = None
        self.bTaula.setEnabled(False)
        self.tipus.setCurrentIndex(0)
        self.soloPrimerItem(self.zona)
        self.calcul.clear()
        self.filtre.clear()

    def nouArxiu(self):
        if self.fCSV is None:
            return

        # Carga combo con zonas si el campo correspondiente está en el fichero CSV
        num = 0
        for zona, val in mv.MAP_ZONES.items():
            if val[1] != '' and self.fCSV.prefixe + QvSqlite.getAlias(val[0]) in self.fCSV.camps:
                self.zona.addItem(zona)
                num = num + 1

        # Comprobar si la extensión del mapa está limitada
        if num > 0:
            extensio = self.fCSV.testExtensioArxiu(mv.MAP_EXTENSIO)
            if extensio:  # Mapa limitado
                self.comboDelete(self.zona, mv.MAP_TRUE_EXTENSIO)
            else:  # Mapa completo
                self.comboDelete(self.zona, mv.MAP_FALSE_EXTENSIO)

        # Ajustar combo de zonas
        if num == 0:
            self.msgInfo("El fitxer " + self.fCSV.fZones + " no té cap camp de zona")
            if hasattr(self, 'arxiu'):
                self.arxiu.lineEdit().clear()
                self.arxiu.setFocus()
            return
        if num == 1:
            self.zona.setCurrentIndex(1)
            self.capa.setFocus()
        else:
            self.zona.setFocus()

        self.taulaMostra = QvEditorCsv(self.fCSV.fZones, [], 'utf-8', self.fCSV.separador, self)
        self.taulaMostra.setWindowTitle("Vista prèvia d'arxiu geocodificat")
        self.taulaMostra.setReadOnly(True)

        self.bTaula.setEnabled(True)
        self.calcul.setItems(self.fCSV.camps, primer='')
        self.filtre.setItems(self.fCSV.camps)

    @pyqtSlot(str)
    def arxiuSeleccionat(self, nom):
        if nom == '':
            return
        self.borrarArxiu()
        self.fCSV = QvMapificacio(nom)
        self.nouArxiu()

    def validaSortida(self, nom):
        fSalida = self.fCSV.nomArxiuSortida(self.fCSV.netejaString(nom, True))
        return self.msgSobreescriure(fSalida)

    def valida(self):
        ok = False
        if hasattr(self, 'arxiu') and self.arxiu.filePath() == '':
            self.msgInfo("S'ha de seleccionar un arxiu de dades")
            self.arxiu.setFocus()
        elif self.zona.currentIndex() <= 0:
            self.msgInfo("S'ha de seleccionar una zona")
            self.zona.setFocus()
        elif self.capa.text().strip() == '':
            self.msgInfo("S'ha de introduir un nom de capa")
            self.capa.setFocus()
        elif self.tipus.currentIndex() <= 0:
            self.msgInfo("S'ha de seleccionar un tipus d'agregació")
            self.tipus.setFocus()
        elif self.calcul.currentText().strip() == '' and self.tipus.currentText() != 'Recompte':
            self.msgInfo("S'ha de introduir un cálcul per fer l'agregació")
            self.calcul.setFocus()
        elif self.fCSV is None:
            return self.msgInfo("No hi ha cap fitxer seleccionat")
        elif not self.validaSortida(self.capa.text().strip()):
            self.capa.setFocus()
        else:
            ok = True
        return ok

    def setRenderParams(self):
        self.renderParams = QvMapRendererParams(self.mapa.currentText())
        if self.simple:
            self.renderParams.colorBase = mv.MAP_COLORS[self.renderParams.colorBase]
        else:
            self.renderParams.colorBase = mv.MAP_COLORS[self.color.currentText()]
        if self.renderParams.colorContorn is None or self.renderParams.colorContorn == 'Base':
            self.renderParams.colorContorn = self.renderParams.colorBase
        else:
            self.renderParams.colorContorn = mv.MAP_CONTORNS[self.renderParams.colorContorn]
        if self.tipus.currentText().startswith('Recompte') and \
           self.distribucio.currentText() == "Total":
            self.renderParams.numDecimals = 0
        else:
            self.renderParams.numDecimals = 2
        if self.renderParams.tipusMapa == 'Àrees':
            self.renderParams.modeCategories = mv.MAP_METODES[self.metode.currentText()]
            self.renderParams.numCategories = self.intervals.value()
        if self.renderParams.tipusMapa == 'Cercles':
            zona = self.zona.currentText()
            if zona == 'Districte':
                self.renderParams.increase = 8
            elif zona == 'Barri':
                self.renderParams.increase = 4
            elif zona == 'Àrea estadística bàsica':
                self.renderParams.increase = 3
            elif zona == 'Secció censal':
                self.renderParams.increase = 2
            else:
                self.renderParams.increase = 1

    def procesa(self):
        if self.taulaMostra is not None:
            self.taulaMostra.hide()
        self.setRenderParams()
        ok = self.fCSV.agregacio(self.llegenda, self.capa.text().strip(),
                                 self.zona.currentText(), self.tipus.currentText(),
                                 self.renderParams,
                                 campAgregat=self.calcul.currentText().strip(),
                                 simple=self.simple,
                                 filtre=self.filtre.currentText().strip(),
                                 tipusDistribucio=self.distribucio.currentText(),
                                 form=self)
        if ok:
            return ''
        else:
            return self.fCSV.msgError
class ImportDialog(QDialog):
    def __init__(self, parent, repo=None, layer=None):
        super(ImportDialog, self).__init__(parent)
        self.setObjectName("ImportDialog")
        self.repo = repo
        self.layer = layer
        self.ok = False
        self.initGui()

    def initGui(self):
        self.setWindowTitle('Import to GeoGig')
        verticalLayout = QVBoxLayout()

        if self.repo is None:
            repos = repository.repos
            layerLabel = QLabel('Repository')
            verticalLayout.addWidget(layerLabel)
            self.repoCombo = QComboBox()
            self.repoCombo.addItems(
                ["%s - %s" % (r.group, r.title) for r in repos])
            self.repoCombo.currentIndexChanged.connect(self.updateBranches)
            verticalLayout.addWidget(self.repoCombo)
        if self.layer is None:
            layerLabel = QLabel('Layer')
            verticalLayout.addWidget(layerLabel)
            self.layerCombo = QComboBox()
            layerNames = [
                layer.name() for layer in vectorLayers()
                if layer.source().lower().split("|")[0].split(".")[-1] in
                ["gpkg", "geopkg"] and not isRepoLayer(layer)
            ]
            self.layerCombo.addItems(layerNames)
            verticalLayout.addWidget(self.layerCombo)

        self.branchLabel = QLabel("Branch")
        verticalLayout.addWidget(self.branchLabel)

        self.branchCombo = QComboBox()
        self.branches = self.repo.branches(
        ) if self.repo is not None else repos[0].branches()
        self.branchCombo.addItems(self.branches)
        verticalLayout.addWidget(self.branchCombo)

        messageLabel = QLabel('Message to describe this update')
        verticalLayout.addWidget(messageLabel)

        self.messageBox = QPlainTextEdit()
        verticalLayout.addWidget(self.messageBox)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel)
        self.importButton = QPushButton("Add layer")
        self.importButton.clicked.connect(self.importClicked)
        self.buttonBox.addButton(self.importButton, QDialogButtonBox.ApplyRole)
        self.buttonBox.rejected.connect(self.cancelPressed)
        verticalLayout.addWidget(self.buttonBox)

        self.setLayout(verticalLayout)

        self.resize(600, 300)

        self.messageBox.setFocus()

    def updateBranches(self):
        self.branchCombo.clear()
        repo = repository.repos[self.repoCombo.currentIndex()]
        self.branches = repo.branches()
        self.branchCombo.addItems(self.branches)

    def importClicked(self):

        ret = QMessageBox.warning(
            config.iface.mainWindow(), 'Import warning',
            "Importing a layer will modify the original layer and might cause data loss.\n"
            "Make sure you have a backup copy of your layer before importing.\n"
            "Do you want to import the selected layer?",
            QMessageBox.Yes | QMessageBox.No)
        if ret == QMessageBox.No:
            return

        if self.repo is None:
            self.repo = repository.repos[self.repoCombo.currentIndex()]
        if self.layer is None:
            text = self.layerCombo.currentText()
            self.layer = layerFromName(text)

        user, email = config.getUserInfo()
        if user is None:
            self.close()
            return
        message = self.messageBox.toPlainText() or datetime.now().strftime(
            "%Y-%m-%d %H_%M_%S")
        branch = self.branchCombo.currentText()
        try:
            self.repo.importgeopkg(self.layer, branch, message, user, email,
                                   False)
            filename, layername = namesFromLayer(self.layer)
            self.repo.checkoutlayer(filename, layername, ref=branch)
            self.layer.reload()
            self.layer.triggerRepaint()
        except GeoGigException as e:
            iface.messageBar().pushMessage("Error",
                                           str(e),
                                           level=QgsMessageBar.CRITICAL,
                                           duration=5)
            self.close()
            return

        addTrackedLayer(self.layer, self.repo.url)

        self.ok = True
        iface.messageBar().pushMessage(
            "Layer was correctly added to repository",
            level=QgsMessageBar.INFO,
            duration=5)
        self.close()

    def cancelPressed(self):
        self.close()
class RemotesDialog(QDialog):
    def __init__(self, parent, repo):
        QDialog.__init__(self, parent,
                         Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.changed = False
        self.repo = repo
        self.remotes = repo.remotes()
        self.setupUi()

    def setupUi(self):
        self.resize(500, 350)
        self.setWindowTitle("Remotes manager")
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setSpacing(2)
        self.horizontalLayout.setMargin(0)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Vertical)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
        self.table = QTableWidget()
        self.table.verticalHeader().setVisible(False)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.addRowButton = QPushButton()
        self.addRowButton.setText("Add remote")
        self.editRowButton = QPushButton()
        self.editRowButton.setText("Edit remote")
        self.removeRowButton = QPushButton()
        self.removeRowButton.setText("Remove remote")
        self.buttonBox.addButton(self.addRowButton,
                                 QDialogButtonBox.ActionRole)
        self.buttonBox.addButton(self.editRowButton,
                                 QDialogButtonBox.ActionRole)
        self.buttonBox.addButton(self.removeRowButton,
                                 QDialogButtonBox.ActionRole)
        self.setTableContent()
        self.horizontalLayout.addWidget(self.table)
        self.horizontalLayout.addWidget(self.buttonBox)
        self.setLayout(self.horizontalLayout)

        self.buttonBox.rejected.connect(self.close)
        self.editRowButton.clicked.connect(self.editRow)
        self.addRowButton.clicked.connect(self.addRow)
        self.removeRowButton.clicked.connect(self.removeRow)

        QMetaObject.connectSlotsByName(self)
        self.editRowButton.setEnabled(False)
        self.removeRowButton.setEnabled(False)

    def setTableContent(self):
        self.table.clear()
        self.table.setColumnCount(2)
        self.table.setColumnWidth(0, 200)
        self.table.setColumnWidth(1, 200)
        self.table.setHorizontalHeaderLabels(["Name", "URL"])
        self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch)
        self.table.setRowCount(len(self.remotes))
        for i, name in enumerate(self.remotes):
            url = self.remotes[name]
            self.table.setRowHeight(i, 22)
            item = QTableWidgetItem(name, 0)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.table.setItem(i, 0, item)
            item = QTableWidgetItem(url, 0)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.table.setItem(i, 1, item)

        self.table.itemSelectionChanged.connect(self.selectionChanged)

    def selectionChanged(self):
        enabled = len(self.table.selectedItems()) > 0
        self.editRowButton.setEnabled(enabled)
        self.removeRowButton.setEnabled(enabled)

    def editRow(self):
        item = self.table.item(self.table.currentRow(), 0)
        if item is not None:
            name = item.text()
            url = self.table.item(self.table.currentRow(), 1).text()
            dlg = NewRemoteDialog(name, url, self)
            dlg.exec_()
            if dlg.ok:
                self.repo.removeremote(name)
                self.repo.addremote(dlg.name, dlg.url)
                del self.remotes[name]
                self.remotes[dlg.name] = dlg.url
                self.setTableContent()
                self.changed = True

    def removeRow(self):
        item = self.table.item(self.table.currentRow(), 0)
        if item is not None:
            name = item.text()
            self.repo.removeremote(name)
            del self.remotes[name]
            self.setTableContent()
            self.changed = True

    def addRow(self):
        dlg = NewRemoteDialog(parent=self)
        dlg.exec_()
        if dlg.ok:
            self.repo.addremote(dlg.name, dlg.url)
            self.remotes[dlg.name] = dlg.url
            self.setTableContent()
            self.changed = True
Exemple #12
0
class AdvancedSearch(EntityEditorDialog):
    search_triggered = pyqtSignal(dict)

    def __init__(self, entity, parent, initial_values: dict = None):
        super().__init__(entity, parent=parent)
        self.parent = parent

        self.initial_values = initial_values or {}

        for k, v in self.initial_values.items():
            for mapper in self._attrMappers:
                if mapper.attributeName() == k:
                    mapper.valueHandler().setValue(v)

    def _init_gui(self, show_str_tab: bool, is_party_unit: bool=False):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(
            self.vlNotification, 0, 0, 1, 1
        )
        column_widget_area = self._setup_columns_content_area()
        self.gridLayout.addWidget(
            column_widget_area, 1, 0, 1, 1
        )
        # Add notification for mandatory columns if applicable
        next_row = 2
        # Set title
        search_trans = self.tr('Advanced Search')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        title = '{0} {1}'.format(title_str, search_trans)
        self.do_not_check_dirty = True
        self.setWindowTitle(title)

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(
            self.buttonBox, next_row, 0, 1, 1
        )

        self.buttonBox.setOrientation(Qt.Horizontal)

        self.search = QPushButton(
            QApplication.translate(
                'EntityEditorDialog', 'Search'
            )
        )
        self.buttonBox.addButton(
            self.search, QDialogButtonBox.ActionRole
        )
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Close | QDialogButtonBox.Reset
        )
        self.search.clicked.connect(self.on_search)
        self.buttonBox.button(QDialogButtonBox.Close).clicked.connect(self.reject)
        self.buttonBox.button(QDialogButtonBox.Reset).clicked.connect(self.reset)

    def reset(self):
        """
        Resets the dialog back to an empty/no filter status
        """
        for column in self._entity.columns.values():
            if column.name in entity_display_columns(self._entity):
                if column.name == 'id':
                    continue
                handler = self.attribute_mappers[
                    column.name].valueHandler()
                handler.setValue(handler.default())

    def on_search(self):
        """
        Builds a dictionary of the desired search values and emits the search_triggered signal
        """
        self.search_triggered.emit(self.current_search_data())

    def current_search_data(self) -> dict:
        """
        Returns a dictionary representing the current search data
        """
        search_data = {}
        for column in self._entity.columns.values():
            if column.name in entity_display_columns(self._entity):
                if column.name == 'id':
                    continue
                handler = self.attribute_mappers[
                    column.name].valueHandler()
                value = handler.value()
                if value != handler.default() and bool(value):
                    search_data[column.name] = value
        return search_data

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for column_name, column_widget in self.column_widgets.items():
            c = self.columns[column_name]

            if c.name in self.exclude_columns:
                continue
            if isinstance(c, MultipleSelectColumn):
                continue
            if c.name not in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                if c.TYPE_INFO == 'AUTO_GENERATED':
                    column_widget.setReadOnly(False)
                    column_widget.btn_load.hide()
                self.gl.addWidget(column_widget, row_id, 1, 1, 1)

                col_name = c.name

                # Add widget to MapperMixin collection
                self.addMapping(
                    col_name,
                    column_widget,
                    c.mandatory,
                    pseudoname=c.ui_display()
                )

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)
        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()
        # self.entity_tab_widget.setTabEnabled(0, False)  # enable/disable the tab
        # set the style sheet
        self.setStyleSheet(
            "QTabBar::tab::selected {width: 0; height: 0; margin: 0; "
            "padding: 0; border: none;} ")
        # Return the correct widget
        if self.entity_tab_widget is not None:
            return self.entity_tab_widget

        return self.entity_scroll_area
Exemple #13
0
class EntityEditorDialog(MapperMixin):
    """
    Dialog for editing entity attributes.
    """
    addedModel = pyqtSignal(object)

    def __init__(self,
                 entity,
                 model=None,
                 parent=None,
                 manage_documents=True,
                 collect_model=False,
                 parent_entity=None,
                 exclude_columns=None,
                 plugin=None,
                 allow_str_creation=True):
        """
        Class constructor.
        :param entity: Entity object corresponding to a table object.
        :type entity: Entity
        :param model: Data object for loading data into the form widgets.
        If the model is set, then the editor dialog is assumed to be in edit
        mode.
        :type model: object
        :param parent: Parent widget that the form belongs to.
        :type parent: QWidget
        :param manage_documents: True if the dialog should provide controls
        for managing supporting documents. Only applicable if the entity
        allows for supporting documents to be attached.
        :type manage_documents: bool
        :param collect_model: If set to True only returns
        the filled form model without saving it to the database.
        :type collect_model: Boolean
        :param parent_entity: The parent entity of the editor
        :type parent_entity: Object
        :param exclude_columns: List of columns to be excluded if in a list.
        :type exclude_columns: List
        :return: If collect_model, returns SQLAlchemy Model
        """
        super().__init__(parent=parent, model=model, entity=entity)

        QgsGui.enableAutoGeometryRestore(self)

        self.collection_suffix = self.tr('Collection')

        # Set minimum width
        self.setMinimumWidth(450)

        self.plugin = plugin

        # Flag for mandatory columns
        self.has_mandatory = False
        self.reload_form = False
        self._entity = entity
        self.edit_model = model
        self.column_widgets = OrderedDict()
        self.columns = {}
        self._parent = parent
        self.exclude_columns = exclude_columns or []
        self.entity_tab_widget = None
        self._disable_collections = False
        self.filter_val = None
        self.parent_entity = parent_entity
        self.child_models = OrderedDict()
        self.entity_scroll_area = None
        self.entity_editor_widgets = OrderedDict()
        self.details_tree_view = None
        # Set notification layout bar
        self.vlNotification = QVBoxLayout()
        self.vlNotification.setObjectName('vlNotification')
        self._notifBar = NotificationBar(self.vlNotification)
        self.do_not_check_dirty = False
        # Set manage documents only if the entity supports documents
        if self._entity.supports_documents:
            self._manage_documents = manage_documents
        else:
            self._manage_documents = False

        # Setup entity model
        self._ent_document_model = None
        if self._entity.supports_documents:
            self.ent_model, self._ent_document_model = entity_model(
                self._entity, with_supporting_document=True)
        else:
            self.ent_model = entity_model(self._entity)
        if model is not None:
            self.ent_model = model

        MapperMixin.__init__(self, self.ent_model, entity)

        self.collect_model = collect_model

        self.register_column_widgets()

        try:
            if isinstance(parent._parent, EntityEditorDialog):
                # hide collections form child editor
                self._disable_collections = True
        except AttributeError:
            self._parent._parent = None

        # Set title
        editor_trans = self.tr('Editor')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        self.title = '{0} {1}'.format(title_str, editor_trans)

        self.setWindowTitle(self.title)

        # determine whether the entity is part of the STR relationship
        curr_profile = current_profile()
        self.participates_in_str, self.is_party_unit = curr_profile.social_tenure.entity_participates_in_str(
            self.entity)

        self._init_gui(show_str_tab=allow_str_creation
                       and self.participates_in_str,
                       is_party_unit=self.is_party_unit)
        self.adjustSize()

        self._get_entity_editor_widgets()

        if isinstance(parent._parent, EntityEditorDialog):
            self.parent_entity = parent.parent_entity
            self.set_parent_values()
            # make the size smaller to differentiate from parent and as it
            # only has few tabs.
            self.adjustSize()

        self.attribute_mappers = self._attr_mapper_collection

        # Exception title for editor extension exceptions
        self._ext_exc_msg = self.tr(
            'An error has occured while executing Python code in the editor '
            'extension:')

        # Register custom editor extension if specified
        self._editor_ext = entity_dlg_extension(self)
        if self._editor_ext is not None:
            self._editor_ext.post_init()

            # Initialize CascadingFieldContext objects
            self._editor_ext.connect_cf_contexts()

    def _init_gui(self, show_str_tab: bool, is_party_unit: bool):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1)

        # set widgets values
        column_widget_area = self._setup_columns_content_area()

        self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1)

        if show_str_tab:
            self._setup_str_tab(is_party_unit=is_party_unit)

        # Add notification for mandatory columns if applicable
        next_row = 2
        if self.has_mandatory:
            self.required_fields_lbl = QLabel(self)
            msg = self.tr('Please fill out all required (*) fields.')
            msg = self._highlight_asterisk(msg)
            self.required_fields_lbl.setText(msg)
            self.gridLayout.addWidget(self.required_fields_lbl, next_row, 0, 1,
                                      2)
            # Bump up row reference
            next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(self.buttonBox, next_row, 0, 1, 1)

        self.buttonBox.setOrientation(Qt.Horizontal)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                          | QDialogButtonBox.Save)

        if self.edit_model is None:
            if not self.collect_model:
                self.save_new_button = QPushButton(
                    QApplication.translate('EntityEditorDialog',
                                           'Save and New'))
                self.buttonBox.addButton(self.save_new_button,
                                         QDialogButtonBox.ActionRole)

        # edit model, collect model
        # adding new record for child

        # Saving in parent editor
        if not isinstance(self._parent._parent, EntityEditorDialog):
            # adding a new record
            if self.edit_model is None:
                # saving when digitizing.
                if self.collect_model:
                    self.buttonBox.accepted.connect(self.on_model_added)
                # saving parent editor
                else:
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                    self.save_new_button.clicked.connect(self.save_and_new)
            # updating existing record
            else:
                if not self.collect_model:
                    # updating existing record of the parent editor
                    self.buttonBox.accepted.connect(self.save_parent_editor)
                else:
                    self.buttonBox.accepted.connect(self.on_model_added)
        # Saving in child editor
        else:
            # save and new record
            if self.edit_model is None:
                self.buttonBox.accepted.connect(self.on_child_saved)
                self.save_new_button.clicked.connect(
                    lambda: self.on_child_saved(True))

            else:
                # When updating an existing child editor save to the db
                self.buttonBox.accepted.connect(self.on_child_saved)
                # self.buttonBox.accepted.connect(self.submit)

        self.buttonBox.rejected.connect(self.cancel)

    @property
    def notification_bar(self):
        """
        :return: Returns the dialog's notification bar.
        :rtype: NotificationBar
        """
        return self._notifBar

    def save_parent_editor(self):
        """
        Saves the parent editor and its children.
        """
        self.submit()
        self.save_children()

    def set_parent_values(self):
        """
        Sets the parent display column for the child.
        """
        if self.parent_entity is None:
            return
        for col in self._entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self.parent_entity:
                    self.parent_widgets_value_setter(self._parent._parent, col)

    def parent_widgets_value_setter(self, parent, col):
        """
        Finds and sets the value from parent widget and set it to the column
        widget of a child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        for parent_col_name, parent_widget in parent.column_widgets.items():
            parent_col = parent.columns[parent_col_name]
            if parent_col.name == col.name:
                self.single_parent_value_setter(col, parent_widget)
                break
            if parent_col.name in col.entity_relation.display_cols:
                self.single_parent_value_setter(col, parent_widget)
                break

    def single_parent_value_setter(self, col, parent_widget):
        """
        Gets value from parent widget and set it to the column widget of a
        child using the child column.
        :param parent: The parent widget
        :type parent: QWidget
        :param col: The child column object
        :type col: Object
        """
        local_widget = self.column_widgets[col.name]
        local_widget.show_clear_button()
        self.filter_val = parent_widget.text()
        local_widget.setText(self.filter_val)

    def save_and_new(self):
        """
        A slot raised when Save and New button is click. It saves the form
        without showing a success message. Then it sets reload_form property
        to True so that entity_browser can re-load the form.
        """
        from stdm.ui.entity_browser import (EntityBrowserWithEditor)
        self.submit(False, True)
        self.save_children()

        if self.is_valid:
            self.addedModel.emit(self.model())
            self.setModel(self.ent_model())
            self.clear()
            self.child_models.clear()
            for index in range(0, self.entity_tab_widget.count() - 1):
                if isinstance(self.entity_tab_widget.widget(index),
                              EntityBrowserWithEditor):
                    child_browser = self.entity_tab_widget.widget(index)
                    child_browser.remove_rows()

    def on_model_added(self):
        """
        A slot raised when a form is submitted with collect model set to True.
        There will be no success message and the form does not close.
        """
        self.submit(True)
        self.addedModel.emit(self.model())

    def closeEvent(self, event):
        """
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        """

        if self.do_not_check_dirty:
            event.accept()
            return
        isDirty, userResponse = self.checkDirty()

        if isDirty:
            if userResponse == QMessageBox.Yes:
                # We need to ignore the event so that validation and
                # saving operations can be executed
                event.ignore()
                self.submit()
            elif userResponse == QMessageBox.No:
                event.accept()
            elif userResponse == QMessageBox.Cancel:
                event.ignore()
        else:
            event.accept()

    def on_child_saved(self, save_and_new=False):
        """
        A slot raised when the save or save and new button is clicked. It sets
        the child_models dictionary of the parent when saved.
        :param save_and_new: A boolean indicating the save and new button is
        clicked to trigger the slot.
        :type save_and_new: Boolean
        """
        if self.parent_entity is None:
            return

        self.submit(True)

        insert_pos = self._parent.tbEntity.model().rowCount() + 1
        # Save to parent editor so that it is persistent.
        self._parent._parent.child_models[insert_pos, self._entity.name] = \
            (self._entity, self.model())
        self.addedModel.emit(self.model())
        if not save_and_new:
            self.accept()

        else:
            if self.is_valid:
                # self.addedModel.emit(self.model())
                self.setModel(self.ent_model())

                self.clear()
                self.set_parent_values()

    def save_children(self):
        """
        Saves children models into the database by assigning the the id of the
        parent for foreign key column.
        """
        if len(self.child_models) < 1:
            return
        children_obj = []
        for row_entity, row_value in self.child_models.items():
            entity, model = row_value

            ent_model = entity_model(entity)
            entity_obj = ent_model()
            for col in entity.columns.values():
                if col.TYPE_INFO == 'FOREIGN_KEY':
                    if col.parent.name == self._entity.name:
                        setattr(model, col.name, self.model().id)
                        children_obj.append(model)
            entity_obj.saveMany(children_obj)

    def register_column_widgets(self):
        """
        Registers the column widgets.
        """
        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        self.scroll_widget_contents = QWidget()
        self.scroll_widget_contents.setObjectName('scrollAreaWidgetContents')
        for c in self._entity.columns.values():
            if c.name in self.exclude_columns:
                continue
            if c.name not in columns and not isinstance(c, VirtualColumn):
                continue
            # Get widget factory
            column_widget = ColumnWidgetRegistry.create(
                c, self.scroll_widget_contents, host=self)
            self.columns[c.name] = c
            self.column_widgets[c.name] = column_widget

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for column_name, column_widget in self.column_widgets.items():
            c = self.columns[column_name]
            if c.name in self.exclude_columns:
                continue
            if c.name not in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                # Format label text if it is a mandatory field
                if c.mandatory:
                    header = '{0} *'.format(c.ui_display())
                    # Highlight asterisk
                    header = self._highlight_asterisk(header)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                self.column_widget = column_widget
                self.gl.addWidget(self.column_widget, row_id, 1, 1, 1)

                # Add user tip if specified for the column configuration
                if c.user_tip:
                    self.tip_lbl = UserTipLabel(user_tip=c.user_tip)
                    self.gl.addWidget(self.tip_lbl, row_id, 2, 1, 1)

                if c.mandatory and not self.has_mandatory:
                    self.has_mandatory = True

                col_name = c.name
                # Replace name accordingly based on column type
                if isinstance(c, MultipleSelectColumn):
                    col_name = c.model_attribute_name

                # Add widget to MapperMixin collection
                self.addMapping(col_name,
                                self.column_widget,
                                c.mandatory,
                                pseudoname=c.ui_display())

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)

        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()

        if not self._disable_collections:
            ch_entities = self.children_entities()

            for col, ch in ch_entities:
                if hasattr(col.entity_relation, 'show_in_parent'):
                    if col.entity_relation.show_in_parent != '0':
                        self._add_fk_browser(ch, col)
                else:
                    self._add_fk_browser(ch, col)

        # Add tab widget if entity supports documents
        if self._entity.supports_documents:
            self.doc_widget = SupportingDocumentsWidget(
                self._entity.supporting_doc, self._ent_document_model, self)

            # Map the source document manager object
            self.addMapping('documents',
                            self.doc_widget.source_document_manager)

            #
            # # Add attribute tab
            # self._add_primary_attr_widget()

            # Add supporting documents tab
            self.entity_tab_widget.addTab(self.doc_widget,
                                          self.tr('Supporting Documents'))

        # Return the correct widget
        if self.entity_tab_widget is not None:
            return self.entity_tab_widget

        return self.entity_scroll_area

    def _add_primary_attr_widget(self):
        # Check if the primary entity
        # exists and add if it does not
        pr_txt = self.tr('Primary')
        if self.entity_tab_widget is not None:
            tab_txt = self.entity_tab_widget.tabText(0)
            if not tab_txt == pr_txt:
                self.entity_tab_widget.addTab(self.entity_scroll_area, pr_txt)

    def _setup_str_tab(self, is_party_unit: bool):
        """
        Creates the STR relationship tab
        """
        from stdm.ui.feature_details import DetailsTreeView

        layout = QVBoxLayout()
        hl = QHBoxLayout()

        add_btn = QToolButton(self)
        add_btn.setText(self.tr('Create STR'))
        add_btn.setIcon(GuiUtils.get_icon('add.png'))
        hl.addWidget(add_btn)
        add_btn.clicked.connect(self._create_str)

        edit_btn = QToolButton(self)
        edit_btn.setText(self.tr('Edit'))
        edit_btn.setIcon(GuiUtils.get_icon('edit.png'))
        edit_btn.setDisabled(True)
        hl.addWidget(edit_btn)

        view_document_btn = QToolButton(self)
        view_document_btn.setText(self.tr('View Supporting Documents'))
        view_document_btn.setIcon(GuiUtils.get_icon('document.png'))
        view_document_btn.setDisabled(True)
        hl.addWidget(view_document_btn)

        hl.addStretch()
        layout.addLayout(hl)

        self.details_tree_view = DetailsTreeView(
            parent=self,
            plugin=self.plugin,
            edit_button=edit_btn,
            view_document_button=view_document_btn)
        self.details_tree_view.activate_feature_details(
            True, follow_layer_selection=False)
        self.details_tree_view.model.clear()

        if is_party_unit:
            self.details_tree_view.search_party(self.entity,
                                                [self.ent_model.id])
        else:
            self.details_tree_view.search_spatial_unit(self.entity,
                                                       [self.ent_model.id])

        layout.addWidget(self.details_tree_view)
        w = QWidget()
        w.setLayout(layout)

        self.entity_tab_widget.addTab(w, self.tr('STR'))

    def _create_str(self):
        """
        Opens the dialog to create a new STR
        """
        from stdm.ui.social_tenure.str_editor import STREditor
        add_str_window = STREditor()

        #if is_party_unit:
        #    add_str.set_party_data(self.entity_model_obj)

        if add_str_window.exec_():
            # STR created - refresh STR view
            if self.is_party_unit:
                self.details_tree_view.search_party(self.entity,
                                                    [self.ent_model.id])
            else:
                self.details_tree_view.search_spatial_unit(
                    self.entity, [self.ent_model.id])

    def _add_fk_browser(self, child_entity, column):
        # Create and add foreign key
        # browser to the collection
        from stdm.ui.entity_browser import (ContentGroupEntityBrowser)

        attr = '{0}_collection'.format(child_entity.name)

        # Return if the attribute does not exist
        if not hasattr(self._model, attr):
            return

        table_content = TableContentGroup(User.CURRENT_USER.UserName,
                                          child_entity.short_name)

        if self.edit_model is not None:
            parent_id = self.edit_model.id
        else:
            parent_id = 0

        entity_browser = ContentGroupEntityBrowser(child_entity,
                                                   table_content,
                                                   rec_id=parent_id,
                                                   parent=self,
                                                   plugin=self.plugin,
                                                   load_recs=False)

        # entity_browser = EntityBrowserWithEditor(
        # child_entity,
        # self,
        # MANAGE,
        # False,
        # plugin=self.plugin
        # )

        entity_browser.buttonBox.setVisible(False)
        entity_browser.record_filter = []

        if len(child_entity.label) > 2:
            column_label = child_entity.label
        else:
            # Split and join  to filter out entity name prefix
            # e.g. 'lo_parcel' to 'parcel'
            column_label = format_name(" ".join(
                child_entity.name.split("_", 1)[1:]))

        self.set_filter(child_entity, entity_browser)

        self.entity_tab_widget.addTab(entity_browser,
                                      '{0}'.format(column_label))

    def set_filter(self, entity, browser):
        col = self.filter_col(entity)
        child_model = entity_model(entity)
        child_model_obj = child_model()
        col_obj = getattr(child_model, col.name)

        browser.filtered_records = []

        if self.model() is not None:
            if self.model().id is not None:
                browser.filtered_records = child_model_obj.queryObject(
                ).filter(col_obj == self.model().id).all()

        if self.edit_model is not None:
            browser.filtered_records = child_model_obj.queryObject().filter(
                col_obj == self.edit_model.id).all()

    def filter_col(self, child_entity):
        for col in child_entity.columns.values():
            if col.TYPE_INFO == 'FOREIGN_KEY':
                parent_entity = col.parent
                if parent_entity == self._entity:
                    return col

    def children_entities(self):
        """
        :return: Returns a list of children entities (by name)
        that refer to the main entity as the parent.
        :rtype: OrderedDict
        """
        child_columns = []
        for ch in self._entity.children():
            if ch.TYPE_INFO == Entity.TYPE_INFO:
                for col in ch.columns.values():
                    if hasattr(col, 'entity_relation'):

                        if col.parent.name == self._entity.name:
                            child_columns.append((col, ch))
        return child_columns

    def document_widget(self):
        """
        :return: Returns the widget for managing
        the supporting documents for an entity if enabled.
        :rtype: SupportingDocumentsWidget
        """
        return self.doc_widget

    def source_document_manager(self):
        """
        :return: Returns an instance of the
        SourceDocumentManager only if supporting
        documents are enabled for the given entity. Otherwise,
        None if supporting documents are not enabled.
        :rtype: SourceDocumentManager
        """
        if self.doc_widget is None:
            return None

        return self.doc_widget.source_document_manager

    def _highlight_asterisk(self, text):
        # Highlight asterisk in red
        c = '*'

        # Do not format if there is no asterisk
        if text.find(c) == -1:
            return text

        asterisk_highlight = '<span style=\" color:#ff0000;\">*</span>'
        text = text.replace(c, asterisk_highlight)

        return '<html><head/><body><p>{0}</p></body></html>'.format(text)

    def _custom_validate(self):
        """
        Override of the MapperMixin which enables custom editor extensions to
        inject additional validation before saving form data.
        :return: Return True if the validation was successful,
        otherwise False.
        :rtype: bool
        """
        if self._editor_ext is not None:
            return self._editor_ext.validate()

        # Return True if there is no custom editor extension specified
        return True

    def _post_save(self, model):
        """
        Include additional post-save logic by custom extensions.
        :param model: SQLAlchemy model
        :type model: object
        """
        if self._editor_ext is not None:
            self._editor_ext.post_save(model)

    def _get_entity_editor_widgets(self):
        """
        Gets entity editor widgets and appends them to a dictionary
        """
        if self.entity_tab_widget:
            tab_count = self.entity_tab_widget.count()
            for i in range(tab_count):
                tab_object = self.entity_tab_widget.widget(i)
                tab_text = self.entity_tab_widget.tabText(i)
                self.entity_editor_widgets[tab_text] = tab_object
        else:
            self.entity_editor_widgets['no_tab'] = self.entity_scroll_area
    def __init__(self, district_registry, parent=None):
        super().__init__(parent)

        self.district_registry = district_registry

        self.setWindowTitle(self.tr('Select New {}').format(
            district_registry.type_string_title()))
        layout = QVBoxLayout()
        self.recent_label = QLabel(self.tr('Recently used {}').format(
            district_registry.type_string_sentence_plural()))
        layout.addWidget(self.recent_label)

        self.recent_list = QListWidget()
        self.recent_list.setMaximumHeight(100)
        for d in district_registry.recent_districts_list():
            item = QListWidgetItem(self.district_registry.get_district_title(d))
            item.setData(Qt.UserRole, d)
            self.recent_list.addItem(item)
        layout.addWidget(self.recent_list, 0)

        self.available_label = QLabel(self.tr('Available {}').format(
            district_registry.type_string_sentence_plural()
        ))
        layout.addWidget(self.available_label)
        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(self.tr('Search for {}').format(
            district_registry.type_string_sentence()))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        self.list = QListWidget()
        for title, code in district_registry.district_titles().items():
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, code)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        if self.district_registry.flags() & DistrictRegistry.FLAG_ALLOWS_SPATIAL_SELECT:
            self.select_from_map_button = button_box.addButton(
                self.tr("Select from Map"), QDialogButtonBox.ActionRole)
            self.select_from_map_button.clicked.connect(self.pick_from_map)
        else:
            self.select_from_map_button = None
        self.chose_pick_from_map = False

        self.setLayout(layout)

        self.recent_list.itemSelectionChanged.connect(
            self.recent_list_item_selected)
        self.list.itemSelectionChanged.connect(
            self.list_item_selected)
        self.recent_list.itemDoubleClicked.connect(
            self.accept)
        self.list.itemDoubleClicked.connect(
            self.accept)

        # select most recently used district by default
        if self.recent_list.count() > 0:
            self.recent_list.item(0).setSelected(True)
class RemotesDialog(QDialog):
    def __init__(self, parent, repo):
        QDialog.__init__(self, parent, Qt.WindowSystemMenuHint | Qt.WindowTitleHint)
        self.changed = False
        self.repo = repo
        self.remotes = repo.remotes()
        self.setupUi()

    def setupUi(self):
        self.resize(500, 350)
        self.setWindowTitle("Remote connections manager")
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setSpacing(2)
        self.horizontalLayout.setMargin(0)
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Vertical)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
        self.table = QTableWidget()
        self.table.verticalHeader().setVisible(False)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.addRowButton = QPushButton()
        self.addRowButton.setText("Add connection")
        self.editRowButton = QPushButton()
        self.editRowButton.setText("Edit connection")
        self.removeRowButton = QPushButton()
        self.removeRowButton.setText("Remove connection")
        self.buttonBox.addButton(self.addRowButton, QDialogButtonBox.ActionRole)
        self.buttonBox.addButton(self.editRowButton, QDialogButtonBox.ActionRole)
        self.buttonBox.addButton(self.removeRowButton, QDialogButtonBox.ActionRole)
        self.setTableContent()
        self.horizontalLayout.addWidget(self.table)
        self.horizontalLayout.addWidget(self.buttonBox)
        self.setLayout(self.horizontalLayout)

        self.buttonBox.rejected.connect(self.close)
        self.editRowButton.clicked.connect(self.editRow)
        self.addRowButton.clicked.connect(self.addRow)
        self.removeRowButton.clicked.connect(self.removeRow)

        QMetaObject.connectSlotsByName(self)
        self.editRowButton.setEnabled(False)
        self.removeRowButton.setEnabled(False)

    def setTableContent(self):
        self.table.clear()
        self.table.setColumnCount(2)
        self.table.setColumnWidth(0, 200)
        self.table.setColumnWidth(1, 200)
        self.table.setHorizontalHeaderLabels(["Name", "URL"])
        self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch)
        self.table.setRowCount(len(self.remotes))
        for i, name in enumerate(self.remotes):
            url = self.remotes[name]
            self.table.setRowHeight(i, 22)
            item = QTableWidgetItem(name, 0)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.table.setItem(i, 0, item)
            item = QTableWidgetItem(url, 0)
            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.table.setItem(i, 1, item)

        self.table.itemSelectionChanged.connect(self.selectionChanged)

    def selectionChanged(self):
        enabled = len(self.table.selectedItems()) > 0
        self.editRowButton.setEnabled(enabled)
        self.removeRowButton.setEnabled(enabled)

    def editRow(self):
        item = self.table.item(self.table.currentRow(), 0)
        if item is not None:
            name = item.text()
            url = self.table.item(self.table.currentRow(), 1).text()
            dlg = NewRemoteDialog(name, url, self)
            dlg.exec_()
            if dlg.ok:
                self.repo.removeremote(name)
                self.repo.addremote(dlg.name, dlg.url)
                del self.remotes[name]
                self.remotes[dlg.name] = dlg.url
                self.setTableContent()
                self.changed = True



    def removeRow(self):
        item = self.table.item(self.table.currentRow(), 0)
        if item is not None:
            name = item.text()
            self.repo.removeremote(name)
            del self.remotes[name]
            self.setTableContent()
            self.changed = True

    def addRow(self):
        dlg = NewRemoteDialog(parent = self)
        dlg.exec_()
        if dlg.ok:
            try:
                self.repo.addremote(dlg.name, dlg.url)                
                self.remotes[dlg.name] = dlg.url
                self.setTableContent()
                self.changed = True
            except HTTPError:
                QMessageBox.warning(self, 'Cannot add remote connection',
                "Remote connection could not be added.\n"
                "Ensure that the entered data is correct and the geogig server is running.",
                QMessageBox.Ok)
Exemple #16
0
class AdvancedSearch(EntityEditorDialog):
    def __init__(self, entity, parent):

        EntityEditorDialog.__init__(self, entity, parent=parent)
        self.parent = parent

    def _init_gui(self):
        # Setup base elements
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName('glMain')
        self.gridLayout.addLayout(self.vlNotification, 0, 0, 1, 1)
        QApplication.processEvents()

        column_widget_area = self._setup_columns_content_area()
        self.gridLayout.addWidget(column_widget_area, 1, 0, 1, 1)
        QApplication.processEvents()
        # Add notification for mandatory columns if applicable
        next_row = 2
        # Set title
        search_trans = self.tr('Advanced Search')
        if self._entity.label is not None:
            if self._entity.label != '':
                title_str = self._entity.label
            else:
                title_str = format_name(self._entity.short_name)
        else:
            title_str = format_name(self._entity.short_name)

        title = '{0} {1}'.format(title_str, search_trans)
        self.do_not_check_dirty = True
        self.setWindowTitle(title)
        # if self.has_mandatory:
        #     self.required_fields_lbl = QLabel(self)
        #     msg = self.tr(
        #         'Please fill out all required (*) fields.'
        #     )
        #     msg = self._highlight_asterisk(msg)
        #     self.required_fields_lbl.setText(msg)
        #     self.gridLayout.addWidget(
        #         self.required_fields_lbl, next_row, 0, 1, 2
        #     )
        #     # Bump up row reference
        #     next_row += 1

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setObjectName('buttonBox')
        self.gridLayout.addWidget(self.buttonBox, next_row, 0, 1, 1)

        self.buttonBox.setOrientation(Qt.Horizontal)

        self.search = QPushButton(
            QApplication.translate('EntityEditorDialog', 'Search'))
        self.buttonBox.addButton(self.search, QDialogButtonBox.ActionRole)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.search.clicked.connect(self.on_search)
        #
        #
        # # edit model, collect model
        # # adding new record for child
        #
        # # Saving in parent editor
        # if not isinstance(self._parent._parent, EntityEditorDialog):
        #     # adding a new record
        #     if self.edit_model is None:
        #         # saving when digitizing.
        #         if self.collect_model:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        #         # saving parent editor
        #         else:
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #             self.save_new_button.clicked.connect(self.save_and_new)
        #     # updating existing record
        #     else:
        #         if not self.collect_model:
        #             # updating existing record of the parent editor
        #             self.buttonBox.accepted.connect(self.save_parent_editor)
        #         else:
        #             self.buttonBox.accepted.connect(self.on_model_added)
        # # Saving in child editor
        # else:
        #     # save and new record
        #     if self.edit_model is None:
        #         self.buttonBox.accepted.connect(self.on_child_saved)
        #         self.save_new_button.clicked.connect(
        #             lambda: self.on_child_saved(True)
        #         )
        #
        #     else:
        #         # When updating an existing child editor save to the db
        #         self.buttonBox.accepted.connect(
        #             self.on_child_saved
        #         )
        #         #self.buttonBox.accepted.connect(self.submit)
        #
        self.buttonBox.rejected.connect(self.cancel)

    def on_search(self):
        search_data = {}
        for column in self._entity.columns.values():
            if column.name in entity_display_columns(self._entity):
                if column.name == 'id':
                    continue
                handler = self.attribute_mappers[column.name].valueHandler()
                value = handler.value()
                if value != handler.default() and bool(value):
                    search_data[column.name] = value
        # self.search_db(search_data)
        result = self.search_db_raw(search_data)
        self.parent._tableModel.removeRows(0,
                                           self.parent._tableModel.rowCount())
        if result is not None:
            found = QApplication.translate('AdvancedSearch', 'records found')
            new_title = '{} - {} {}'.format(self.title, result.rowcount, found)
            if result.rowcount > 3000:
                title = QApplication.translate('AdvancedSearch',
                                               'Advanced Search')
                message = QApplication.translate(
                    'AdvancedSearch',
                    'The search result returned {0} records, which is above the '
                    'search result limit. <br>Would you like to see the first 3000 '
                    'records?'.format("{:,}".format(result.rowcount)))

                res, chk_result = simple_dialog(self, title, message)
                if res:
                    self.setWindowTitle(new_title)
                    self.parent._initializeData(result)
                else:
                    return

            else:
                self.setWindowTitle(new_title)
                self.parent._initializeData(result)

    def search_db(self, search_data):
        ent_model_obj = self.ent_model()
        # query = ent_model_obj.queryObject()
        for attr, value in search_data.items():
            ent_model_obj.queryObject().filter(
                getattr(self.ent_model(), attr) == value)

        # now we can run the query
        # print(ent_model_obj.queryObject(), vars(ent_model_obj.queryObject()))
        # print(str(ent_model_obj.queryObject()))
        results = ent_model_obj.queryObject().all()

    def search_db_raw(self, search_data):
        sql = "SELECT * FROM {} WHERE ".format(self._entity.name)
        # query = ent_model_obj.queryObject()
        param = []
        if len(search_data) == 0:
            return None
        for attr, value in search_data.items():
            if isinstance(value, (int, float)):
                param.append('{} = {}'.format(str(attr), str(value)))
            if isinstance(value, str):
                param.append("{} = '{}'".format(str(attr), str(value)))
        final_sql = '{} {}'.format(sql, ' AND '.join(param))
        # sql_text = text(final_sql)
        results = fetch_with_filter(final_sql)
        # now we can run the query

        return results

    def _setup_columns_content_area(self):
        # Only use this if entity supports documents
        # self.entity_tab_widget = None
        self.doc_widget = None

        self.entity_scroll_area = QScrollArea(self)
        self.entity_scroll_area.setFrameShape(QFrame.NoFrame)
        self.entity_scroll_area.setWidgetResizable(True)
        self.entity_scroll_area.setObjectName('scrollArea')

        # Grid layout for controls
        self.gl = QGridLayout(self.scroll_widget_contents)
        self.gl.setObjectName('gl_widget_contents')

        # Append column labels and widgets
        table_name = self._entity.name
        columns = table_column_names(table_name)
        # Iterate entity column and assert if they exist
        row_id = 0
        for column_name, column_widget in self.column_widgets.items():
            c = self.columns[column_name]

            if c.name in self.exclude_columns:
                continue
            if isinstance(c, MultipleSelectColumn):
                continue
            if not c.name in columns and not isinstance(c, VirtualColumn):
                continue

            if column_widget is not None:
                header = c.ui_display()
                self.c_label = QLabel(self.scroll_widget_contents)

                self.c_label.setText(header)
                self.gl.addWidget(self.c_label, row_id, 0, 1, 1)

                if c.TYPE_INFO == 'AUTO_GENERATED':
                    column_widget.setReadOnly(False)
                    column_widget.btn_load.hide()
                self.gl.addWidget(column_widget, row_id, 1, 1, 1)

                col_name = c.name

                # Add widget to MapperMixin collection
                self.addMapping(col_name,
                                column_widget,
                                c.mandatory,
                                pseudoname=c.ui_display())

                # Bump up row_id
                row_id += 1

        self.entity_scroll_area.setWidget(self.scroll_widget_contents)
        if self.entity_tab_widget is None:
            self.entity_tab_widget = QTabWidget(self)
        # Check if there are children and add foreign key browsers

        # Add primary tab if necessary
        self._add_primary_attr_widget()
        # self.entity_tab_widget.setTabEnabled(0, False)  # enable/disable the tab
        # set the style sheet
        self.setStyleSheet(
            "QTabBar::tab::selected {width: 0; height: 0; margin: 0; "
            "padding: 0; border: none;} ")
        # Return the correct widget
        if self.entity_tab_widget is not None:
            return self.entity_tab_widget

        return self.entity_scroll_area

    def closeEvent(self, event):
        '''
        Raised when a request to close the window is received.
        Check the dirty state of input controls and prompt user to
        save if dirty.
        '''
        event.accept()

    def cancel(self):
        '''
        Slot for closing the dialog.
        Checks the dirty state first before closing.
        '''
        self.reject()