def _fill_tbl(self, filter_name=""):
        """ Fill table with initial data into QTableView """

        complet_list = self._get_list("v_ui_workspace", filter_name)

        if complet_list is False:
            return False, False
        for field in complet_list['body']['data']['fields']:
            if 'hidden' in field and field['hidden']: continue
            model = self.tbl_wrkspcm.model()
            if model is None:
                model = QStandardItemModel()
                self.tbl_wrkspcm.setModel(model)
            model.removeRows(0, model.rowCount())

            if field['value']:
                self.tbl_wrkspcm = tools_gw.add_tableview_header(
                    self.tbl_wrkspcm, field)
                self.tbl_wrkspcm = tools_gw.fill_tableview_rows(
                    self.tbl_wrkspcm, field)
            # TODO: config_form_tableview
            # widget = tools_gw.set_tablemodel_config(self.dlg_workspace_manager, self.tbl_wrkspcm, 'tbl_wrkspcm', 1, True)
            tools_qt.set_tableview_config(self.tbl_wrkspcm)

        return complet_list
class HTMLFieldSelectionDialog(QDialog, HTML_FIELDS_CLASS):
    def __init__(self, iface, feat):
        super(HTMLFieldSelectionDialog, self).__init__(iface.mainWindow())
        self.setupUi(self)
        self.iface = iface
        self.feat = feat
        self.selected = []
        self.selectAllButton.clicked.connect(self.selectAll)
        self.clearButton.clicked.connect(self.clearAll)
        self.checkBox.stateChanged.connect(self.initModel)
        self.initModel()

    def initModel(self):
        self.model = QStandardItemModel(self.listView)
        state = self.checkBox.isChecked()
        for key in list(self.feat.keys()):
            if state == False or self.feat[key] > 0:
                item = QStandardItem()
                item.setText(key)
                item.setCheckable(True)
                item.setSelectable(False)
                self.model.appendRow(item)
        self.listView.setModel(self.model)
        self.listView.show()

    def selectAll(self):
        cnt = self.model.rowCount()
        for i in range(0, cnt):
            item = self.model.item(i)
            item.setCheckState(Qt.Checked)

    def clearAll(self):
        cnt = self.model.rowCount()
        for i in range(0, cnt):
            item = self.model.item(i)
            item.setCheckState(Qt.Unchecked)

    def accept(self):
        self.selected = []
        cnt = self.model.rowCount()
        for i in range(0, cnt):
            item = self.model.item(i)
            if item.checkState() == Qt.Checked:
                self.selected.append(item.text())
        self.close()
class SimulationOverview(uicls, basecls):
    """Dialog with methods for handling running simulations."""

    PROGRESS_COLUMN_IDX = 2

    def __init__(self, plugin_dock, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.plugin_dock = plugin_dock
        self.threedi_api = self.plugin_dock.threedi_api
        self.user = self.plugin_dock.current_user
        self.model_selection_dlg = ModelSelectionDialog(self.plugin_dock,
                                                        parent=self)
        self.simulation_init_wizard = None
        self.simulation_wizard = None
        self.simulations_keys = {}
        self.last_progresses = {}
        self.simulations_without_progress = set()
        self.tv_model = None
        self.setup_view_model()
        self.plugin_dock.simulations_progresses_sentinel.progresses_fetched.connect(
            self.update_progress)
        self.pb_new_sim.clicked.connect(self.new_wizard_init)
        self.pb_stop_sim.clicked.connect(self.stop_simulation)

    def setup_view_model(self):
        """Setting up model and columns for TreeView."""
        delegate = SimulationProgressDelegate(self.tv_sim_tree)
        self.tv_sim_tree.setItemDelegateForColumn(self.PROGRESS_COLUMN_IDX,
                                                  delegate)
        self.tv_model = QStandardItemModel(0, 3)
        self.tv_model.setHorizontalHeaderLabels(
            ["Simulation name", "User", "Progress"])
        self.tv_sim_tree.setModel(self.tv_model)

    def add_simulation_to_model(self, simulation, status, progress):
        """Method for adding simulation to the model."""
        sim_id = simulation.id
        sim_name_item = QStandardItem(f"{simulation.name} ({sim_id})")
        sim_name_item.setData(sim_id, Qt.UserRole)
        user_item = QStandardItem(simulation.user)
        progress_item = QStandardItem()
        progress_item.setData((status, progress), PROGRESS_ROLE)
        self.tv_model.appendRow([sim_name_item, user_item, progress_item])
        self.simulations_keys[sim_id] = simulation
        for i in range(self.PROGRESS_COLUMN_IDX):
            self.tv_sim_tree.resizeColumnToContents(i)

    def update_progress(self, progresses):
        """Updating progress bars in the running simulations list."""
        for sim_id, (sim, status, progress) in progresses.items():
            status_name = status.name
            if status_name not in [
                    "queued", "starting", "initialized", "postprocessing"
            ]:
                continue
            if sim_id not in self.simulations_keys:
                self.add_simulation_to_model(sim, status, progress)
        row_count = self.tv_model.rowCount()
        for row_idx in range(row_count):
            name_item = self.tv_model.item(row_idx, 0)
            sim_id = name_item.data(Qt.UserRole)
            if sim_id in self.simulations_without_progress or sim_id not in progresses:
                continue
            progress_item = self.tv_model.item(row_idx,
                                               self.PROGRESS_COLUMN_IDX)
            sim, new_status, new_progress = progresses[sim_id]
            status_name = new_status.name
            if status_name == "stopped" or status_name == "crashed":
                old_status, old_progress = progress_item.data(PROGRESS_ROLE)
                progress_item.setData((new_status, old_progress),
                                      PROGRESS_ROLE)
                self.simulations_without_progress.add(sim_id)
            else:
                progress_item.setData((new_status, new_progress),
                                      PROGRESS_ROLE)
            if status_name == "finished":
                self.simulations_without_progress.add(sim_id)
                msg = f"Simulation {sim.name} finished!"
                self.plugin_dock.communication.bar_info(msg,
                                                        log_text_color=QColor(
                                                            Qt.darkGreen))

    def new_wizard_init(self):
        """Open new simulation initiation options dialog."""
        if self.plugin_dock.current_local_schematisation is not None:
            self.model_selection_dlg.search_le.setText(
                self.plugin_dock.current_local_schematisation.name)
            self.model_selection_dlg.fetch_3di_models()
            self.model_selection_dlg.refresh_templates_list()
        self.model_selection_dlg.exec_()
        if self.model_selection_dlg.model_is_loaded:
            simulation_template = self.model_selection_dlg.current_simulation_template
            simulation, settings_overview, events = self.get_simulation_data_from_template(
                simulation_template)
            self.simulation_init_wizard = SimulationInit(simulation_template,
                                                         settings_overview,
                                                         events,
                                                         parent=self)
            self.simulation_init_wizard.exec_()
            if self.simulation_init_wizard.open_wizard:
                self.new_simulation(simulation, settings_overview, events)

    def get_simulation_data_from_template(self, template):
        """Fetching simulation, settings and events data from the simulation template."""
        simulation, settings_overview, events = None, None, None
        try:
            tc = ThreediCalls(self.threedi_api)
            simulation = template.simulation
            settings_overview = tc.fetch_simulation_settings_overview(
                str(simulation.id))
            events = tc.fetch_simulation_events(simulation.id)
        except ApiException as e:
            error_msg = extract_error_message(e)
            self.plugin_dock.communication.bar_error(error_msg)
        except Exception as e:
            error_msg = f"Error: {e}"
            self.plugin_dock.communication.bar_error(error_msg)
        return simulation, settings_overview, events

    def new_simulation(self, simulation, settings_overview, events):
        """Opening a wizard which allows defining and running new simulations."""
        self.simulation_wizard = SimulationWizard(self.plugin_dock,
                                                  self.model_selection_dlg,
                                                  self.simulation_init_wizard)
        if simulation:
            self.simulation_wizard.load_template_parameters(
                simulation, settings_overview, events)
        self.close()
        self.simulation_wizard.exec_()
        new_simulations = self.simulation_wizard.new_simulations
        if new_simulations is not None:
            for sim in new_simulations:
                initial_status = self.simulation_wizard.new_simulation_statuses.get(
                    sim.id)
                initial_progress = Progress(percentage=0, time=sim.duration)
                self.add_simulation_to_model(sim, initial_status,
                                             initial_progress)

    def stop_simulation(self):
        """Sending request to shut down currently selected simulation."""
        index = self.tv_sim_tree.currentIndex()
        if not index.isValid():
            return
        title = "Warning"
        question = "This simulation is now running.\nAre you sure you want to stop it?"
        answer = self.plugin_dock.communication.ask(self, title, question,
                                                    QMessageBox.Warning)
        if answer is True:
            try:
                name_item = self.tv_model.item(index.row(), 0)
                sim_id = name_item.data(Qt.UserRole)
                tc = ThreediCalls(self.plugin_dock.threedi_api)
                tc.create_simulation_action(sim_id, name="shutdown")
                msg = f"Simulation {name_item.text()} stopped!"
                self.plugin_dock.communication.bar_info(msg)
            except ApiException as e:
                error_msg = extract_error_message(e)
                self.plugin_dock.communication.show_error(error_msg)
            except Exception as e:
                error_msg = f"Error: {e}"
                self.plugin_dock.communication.show_error(error_msg)
class ThinGreyscaleDialog(QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        self.iface = iface
        self.plugin_dir = dirname(__file__)
        self.THINGREYSCALE = self.tr('ThinGreyscale')
        self.BROWSE = self.tr('Browse')
        self.CANCEL = self.tr('Cancel')
        self.CLOSE = self.tr('Close')
        self.HELP = self.tr('Help')
        self.OK = self.tr('OK')
        self.DEFAULTPROVIDER = 'GTiff'
        self.DEFAULTEXTENSION = '.tif'
        self.EXTRAEXTENSION = ' *.tiff'
        super(ThinGreyscaleDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        self.showInfo("Connecting UI components")
        okButton = self.button_box.button(QDialogButtonBox.Ok)
        okButton.setText(self.OK)
        cancelButton = self.button_box.button(QDialogButtonBox.Cancel)
        cancelButton.setText(self.CANCEL)
        cancelButton.setEnabled(False)
        closeButton = self.button_box.button(QDialogButtonBox.Close)
        closeButton.setText(self.CLOSE)
        browseButton = self.browseButton
        browseButton.setText(self.BROWSE)
        self.calcHistPushButton.setEnabled(False)
        self.listModel = QStandardItemModel(self.levelsListView)
        self.levelsListView.setModel(self.listModel)
        self.levelsListView.sizeHintForColumn(20)
        #self.levelValuesCheckBox.setEnabled(False)
        # Help button
        helpButton = self.helpButton
        helpButton.setText(self.HELP)

        # Connect signals
        self.showInfo("Connecting signals")
        okButton.clicked.connect(self.startWorker)
        cancelButton.clicked.connect(self.killWorker)
        closeButton.clicked.connect(self.reject)
        helpButton.clicked.connect(self.help)
        browseButton.clicked.connect(self.browse)

        inpIndexCh = self.inputRaster.currentIndexChanged['QString']
        inpIndexCh.connect(self.layerchanged)
        bandCh = self.bandComboBox.currentIndexChanged['QString']
        bandCh.connect(self.bandChanged)
        #self.iface.legendInterface().itemAdded.connect(
        #    self.layerlistchanged)
        #self.iface.legendInterface().itemRemoved.connect(
        #    self.layerlistchanged)
        #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject)
        self.button_box.rejected.disconnect(self.reject)
        calchistPr = self.calcHistPushButton.clicked
        calchistPr.connect(self.calculateHistogram)
        sugglevPr = self.suggestlevelsPushButton.clicked
        sugglevPr.connect(self.suggestLevels)
        addlevPr = self.addlevelPushButton.clicked
        addlevPr.connect(self.addLevel)
        dellevPr = self.deletelevelsPushButton.clicked
        dellevPr.connect(self.removeLevel)

        maxvalCh = self.maxValueSpinBox.valueChanged
        maxvalCh.connect(self.minmaxvalueChanged)
        maxvalFi = self.maxValueSpinBox.editingFinished
        maxvalFi.connect(self.minmaxvalueEdFinished)
        minvalCh = self.minValueSpinBox.valueChanged
        minvalCh.connect(self.minmaxvalueChanged)
        minvalFi = self.minValueSpinBox.editingFinished
        minvalFi.connect(self.minmaxvalueEdFinished)

        # Set instance variables
        #self.mem_layer = None
        self.worker = None
        self.inputlayerid = None
        self.inputlayer = None
        self.layerlistchanging = False
        self.minvalue = 1
        self.inputrasterprovider = None
        self.histobins = 50
        self.setupScene = QGraphicsScene(self)
        self.histoGraphicsView.setScene(self.setupScene)
        # Is the layer band of an integer type
        self.intband = False
        self.histogramAvailable = False
        self.histo = None
        self.histopadding = 1

    def startWorker(self):
        """Initialises and starts the worker thread."""
        try:
            layerindex = self.inputRaster.currentIndex()
            layerId = self.inputRaster.itemData(layerindex)
            inputlayer = QgsProject.instance().mapLayer(layerId)
            #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
            if inputlayer is None:
                self.showError(self.tr('No input layer defined'))
                return
            # create a reference to the layer that is being processed
            # (for use when creating the resulting raster layer)
            self.thinninglayer = inputlayer
            self.levels = []
            #self.levelsListView.selectAll()
            #selected = self.levelsListView.selectedIndexes()
            if self.levelsListView.model().rowCount() == 0:
                self.showInfo("Levels must be specified!")
                return
            for i in range(self.levelsListView.model().rowCount()):
                levelstring = self.levelsListView.model().item(i).text()
                #for i in selected:
                #    levelstring = self.levelsListView.model().itemData(i)[0]
                if self.intband:
                    self.levels.append(int(levelstring))
                else:
                    self.levels.append(float(levelstring))
            #self.levelsListView.clearSelection()
            # create a new worker instance
            worker = Worker(inputlayer, self.levels, self.intband)
            # configure the QgsMessageBar
            msgBar = self.iface.messageBar().createMessage(
                self.tr('Skeletonising'), '')
            self.aprogressBar = QProgressBar()
            self.aprogressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            acancelButton = QPushButton()
            acancelButton.setText(self.CANCEL)
            acancelButton.clicked.connect(self.killWorker)
            msgBar.layout().addWidget(self.aprogressBar)
            msgBar.layout().addWidget(acancelButton)
            # Has to be popped after the thread has finished (in
            # workerFinished).
            self.iface.messageBar().pushWidget(msgBar, Qgis.Info)
            self.messageBar = msgBar
            # start the worker in a new thread
            thread = QThread(self)
            worker.moveToThread(thread)
            worker.finished.connect(self.workerFinished)
            worker.error.connect(self.workerError)
            worker.status.connect(self.workerInfo)
            worker.progress.connect(self.progressBar.setValue)
            worker.progress.connect(self.aprogressBar.setValue)
            worker.iterprogress.connect(self.iterProgressBar.setValue)
            thread.started.connect(worker.run)
            thread.start()
            self.thread = thread
            self.worker = worker
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
            self.button_box.button(QDialogButtonBox.Close).setEnabled(False)
            self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True)
        except:
            import traceback
            self.showError(traceback.format_exc())
        else:
            pass

    def workerFinished(self, ok, ret):
        """Handles the output from the worker and cleans up after the
           worker has finished."""
        # clean up the worker and thread
        self.showInfo("Handling the result")
        self.worker.deleteLater()
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()
        # remove widget from message bar (pop)
        self.iface.messageBar().popWidget(self.messageBar)
        if ok and ret is not None:
            #self.showInfo("Ret: "+str(ret[10,]))
            # Transformation:
            self.minx = self.thinninglayer.extent().xMinimum()
            self.maxx = self.thinninglayer.extent().xMaximum()
            self.miny = self.thinninglayer.extent().yMinimum()
            self.maxy = self.thinninglayer.extent().yMaximum()
            self.rows = self.thinninglayer.height()
            self.cols = self.thinninglayer.width()
            self.xres = (self.maxx - self.minx) / float(self.cols)
            self.yres = (self.maxy - self.miny) / float(self.rows)
            geotransform = (self.minx, self.xres, 0, self.maxy, 0, -self.yres)
            try:
                format = self.DEFAULTPROVIDER
                driver = gdal.GetDriverByName(format)
                NOVALUE = 0
                metadata = driver.GetMetadata()
                fileName = self.outputRaster.text()
                if self.outputRaster.text() == "":
                    self.showInfo("No output file specified, " +
                                  "creating a temporary file")
                    # Get a temporary file
                    fileName = mktemp(prefix='greyskel',
                                      suffix=self.DEFAULTEXTENSION)
                fileInfo = QFileInfo(fileName)
                filepath = fileInfo.absolutePath()
                baseName = fileInfo.baseName()
                suffix = fileInfo.suffix()
                thisfilename = filepath + baseName + '.' + suffix
                thisfilename = fileName
                self.showInfo("File name: " + thisfilename)
                gdaldatatype = gdal.GDT_Byte
                skelmatrix = None
                if self.levelValuesCheckBox.isChecked():
                    # Transform the pixel values back to the original
                    # level values
                    my_dict = {}
                    # Add zero to handle the "empty" pixels
                    my_dict[0] = 0
                    for i in range(len(self.levels)):
                        my_dict[i + 1] = self.levels[i]
                    skelmatrix = np.vectorize(my_dict.__getitem__,
                                              otypes=[np.float])(ret)
                    gdaldatatype = gdal.GDT_Int32
                    if not self.intband:
                        gdaldatatype = gdal.GDT_Float32
                else:
                    skelmatrix = ret
                outDataset = driver.Create(thisfilename, self.cols, self.rows,
                                           1, gdaldatatype)
                if self.thinninglayer.dataProvider().crs() is not None:
                    srs = self.thinninglayer.dataProvider().crs()
                    outDataset.SetProjection(srs.toWkt().encode(
                        'ascii', 'ignore'))
                skeletonband = outDataset.GetRasterBand(1)
                skeletonband.WriteArray(skelmatrix)
                skeletonband.SetNoDataValue(NOVALUE)
                #stats = skeletonband.GetStatistics(False, True)
                #skeletonband.SetStatistics(stats[0], stats[1],
                #                                 stats[2], stats[3])
                outDataset.SetGeoTransform(geotransform)
                outDataset = None  # To close the file
                # report the result
                rlayer = QgsRasterLayer(thisfilename, baseName)
                self.layerlistchanging = True
                #QgsMapLayerRegistry.instance().addMapLayer(rlayer)
                QgsProject.instance().addMapLayer(rlayer)
                self.layerlistchanging = False
            except:
                import traceback
                self.showError("Can't write the skeleton file:  %s" %
                               self.outputRaster.text() + ' - ' +
                               traceback.format_exc())
                okb = self.button_box.button(QDialogButtonBox.Ok)
                okb.setEnabled(True)
                closb = self.button_box.button(QDialogButtonBox.Close)
                closb.setEnabled(True)
                cancb = self.button_box.button(QDialogButtonBox.Cancel)
                cancb.setEnabled(False)
                return
            QgsMessageLog.logMessage(self.tr('ThinGreyscale finished'),
                                     self.THINGREYSCALE, Qgis.Info)
        else:
            # notify the user that something went wrong
            if not ok:
                self.showError(self.tr('Aborted') + '!')
            else:
                self.showError(self.tr('No skeleton created') + '!')
        self.progressBar.setValue(0.0)
        #self.aprogressBar.setValue(0.0)
        self.iterProgressBar.setValue(0.0)
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
        self.button_box.button(QDialogButtonBox.Close).setEnabled(True)
        self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False)

    def workerError(self, exception_string):
        """Report an error from the worker."""
        #QgsMessageLog.logMessage(self.tr('Worker failed - exception') +
        #                         ': ' + str(exception_string),
        #                         self.THINGREYSCALE,
        #                         QgsMessageLog.CRITICAL)
        self.showError(exception_string)

    def workerInfo(self, message_string):
        """Report an info message from the worker."""
        QgsMessageLog.logMessage(
            self.tr('Worker') + ': ' + message_string, self.THINGREYSCALE,
            Qgis.Info)

    def layerchanged(self, number=0):
        """Do the necessary updates after a layer selection has
           been changed."""
        self.showInfo("Layer changed")
        # If the layer list is being updated, don't do anything
        if self.layerlistchanging:
            return
        layerindex = self.inputRaster.currentIndex()
        layerId = self.inputRaster.itemData(layerindex)
        self.inputlayerid = layerId
        #self.inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
        self.inputlayer = QgsProject.instance().mapLayer(layerId)
        if self.inputlayer is not None:
            self.inputrasterprovider = self.inputlayer.dataProvider()
            self.bandComboBox.clear()
            bandcount = self.inputlayer.bandCount()
            #self.showInfo("Layer bandcount: "+str(bandcount))
            for i in range(bandcount):
                self.bandComboBox.addItem(self.inputlayer.bandName(i + 1), i)
                #self.showInfo("Band " + str(i) + ": " +
                #                self.inputlayer.bandName(i+1))
            # Check if the driver supports Create() or CreateCopy()
            #gdalmetadata = self.inputlayer.metadata()
            #self.showInfo("Layer metadata: " +
            #                str(gdalmetadata.encode('utf-8')))
            #provstring = '<p>GDAL provider</p>\n'
            #providerpos = gdalmetadata.find(provstring)
            #brpos = gdalmetadata.find('<br>', providerpos + len(provstring))
            #self.gdalprovider = gdalmetadata[int(providerpos +
            #                             len(provstring)):int(brpos)]
            #self.showInfo('GDAL provider: '+self.gdalprovider)
            #drivername = self.gdalprovider.encode('ascii', 'ignore')
            #theDriver = gdal.GetDriverByName(drivername)
            #if theDriver is None:
            #    self.showInfo("Unable to get the raster driver")
            #else:
            #data    theMetadata = theDriver.GetMetadata()
            #self.showInfo("Driver metadata: "+str(theMetadata))
            #if ((gdal.DCAP_CREATE in theMetadata) and
            #        theMetadata[gdal.DCAP_CREATE] == 'YES'):
            #    self.canCreate = True
            #if (theMetadata.has_key(gdal.DCAP_CREATECOPY) and
            #if ((gdal.DCAP_CREATECOPY in theMetadata) and
            #        theMetadata[gdal.DCAP_CREATECOPY] == 'YES'):
            #    self.canCreateCopy = True
            #self.showInfo('raster provider type: ' +
            #                str(self.inputlayer.providerType()))
            # Determine the file suffix
            #self.gdalext = ""
            #if gdal.DMD_EXTENSION in theMetadata:
            #    self.gdalext = "." + theMetadata[gdal.DMD_EXTENSION]
            #else:
            #    self.showInfo("No extension available in GDAL metadata")
            # by parsing the layer metadata looking for
            #           "Dataset Description"
            #descstring = 'Dataset Description</p>\n<p>'
            #descpos = gdalmetadata.find(descstring)
            #ppos = gdalmetadata.find('</p>',descpos+len(descstring))
            #filename = gdalmetadata[descpos+len(descstring):ppos]
            #self.gdalext = splitext(filename)[1]
            #self.showInfo('GDAL extension: '+self.gdalext)
            # Determine the datatype
            #datatypestring = 'Data Type</p>\n<p>'
            #datatypepos = gdalmetadata.find(datatypestring)
            #ppos = gdalmetadata.find('</p>',
            #                   datatypepos + len(datatypestring))
            #datatypedesc = gdalmetadata[datatypepos +
            #                            len(datatypestring):ppos]
            #shortdesc = datatypedesc.split()[0]
            #self.showInfo('GDAL data type: GDT_'+shortdesc)
            # Call the findGdalDatatype function
            #self.findGdalDatatype(shortdesc)
            #   self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            self.calcHistPushButton.setEnabled(True)
            self.suggestlevelsPushButton.setEnabled(True)

    def bandChanged(self):
        band = self.bandComboBox.currentIndex() + 1
        self.showInfo("Band changed: " + str(band))
        statistics = self.inputrasterprovider.bandStatistics(band)
        #self.showInfo("Band statistics: " + str(statistics.minimumValue) +
        #                            " - " + str(statistics.maximumValue) +
        #                            " - " + str(statistics.mean))
        self.bandmin = statistics.minimumValue
        self.bandmax = statistics.maximumValue
        dt = self.inputrasterprovider.dataType(band)
        # Integer data type
        if (dt == Qgis.Byte or dt == Qgis.UInt16 or dt == Qgis.Int16
                or dt == Qgis.UInt32 or dt == Qgis.Int32):
            self.intband = True
            self.minValueSpinBox.setDecimals(0)
            self.maxValueSpinBox.setDecimals(0)
            self.levelSpinBox.setDecimals(0)
            self.bandMinLabel.setText(str(int(statistics.minimumValue)))
            self.bandMaxLabel.setText(str(int(statistics.maximumValue)))
        else:
            self.intband = False
            self.minValueSpinBox.setDecimals(5)
            self.maxValueSpinBox.setDecimals(5)
            self.levelSpinBox.setDecimals(5)
            minlabtext = "{0:.5f}".format(statistics.minimumValue)
            self.bandMinLabel.setText(minlabtext)
            maxlabtext = "{0:.5f}".format(statistics.maximumValue)
            self.bandMaxLabel.setText(maxlabtext)
        #self.minValueSpinBox.setMinimum(statistics.minimumValue)
        self.maxValueSpinBox.setMinimum(statistics.minimumValue)
        #self.minValueSpinBox.setMaximum(statistics.maximumValue)
        self.maxValueSpinBox.setMaximum(statistics.maximumValue)
        #self.minValueSpinBox.setValue(statistics.minimumValue)
        if not (statistics.statsGathered & statistics.Mean):
            bandmean = (statistics.minimumValue + statistics.maximumValue) / 2
        else:
            #self.showInfo("statsgathered: " + str(statistics.statsGathered))
            bandmean = statistics.mean
        if self.intband:
            self.minValueSpinBox.setValue(int(ceil(bandmean)))
        else:
            self.minValueSpinBox.setValue(bandmean)
        self.maxValueSpinBox.setValue(statistics.maximumValue)
        self.histMinValue.setText(str(statistics.minimumValue))
        self.histMaxValue.setText(str(statistics.maximumValue))
        self.levelSpinBox.setMinimum(statistics.minimumValue)
        self.levelSpinBox.setMaximum(statistics.maximumValue)
        self.histogramAvailable = False
        #if self.inputrasterprovider.hasStatistics(band):
        #if statistics.statsGathered:
        #histogram = statistics.histogramVector
        #self.showInfo("Histogram: " + str(histogram))
        #range = min to max
        #np.histogram(band, 50, range)

    def minmaxvalueChanged(self):
        #if self.minValueSpinBox is None:
        #    return
        minvalue = self.minValueSpinBox.value()
        #if minvalue is None:
        #    return
        #if self.maxValueSpinBox is None:
        #    return
        maxvalue = self.maxValueSpinBox.value()
        #if maxvalue is None:
        #   return
        if isnan(maxvalue) or isnan(minvalue):
            return
        self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " +
                      str(maxvalue))
        #if self.intband:
        #    minvalue = int(minvalue)
        #    maxvalue = int(maxvalue)
        if abs(maxvalue - minvalue) < 0.00001:
            #if maxvalue == maxvalue:
            self.calcHistPushButton.setEnabled(False)
        else:
            self.calcHistPushButton.setEnabled(True)
        # Update the min and max value spinboxes
        self.minValueSpinBox.setMaximum(maxvalue)
        self.maxValueSpinBox.setMinimum(minvalue)
        self.minValueSpinBox.setMinimum(self.bandmin)

    def minmaxvalueEdFinished(self):
        minvalue = self.minValueSpinBox.value()
        maxvalue = self.maxValueSpinBox.value()
        if self.intband:
            minvalue = int(minvalue)
            maxvalue = int(maxvalue)
        self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " +
                      str(maxvalue))
        # Update the spin box for adding levels
        self.levelSpinBox.setMinimum(minvalue)
        self.levelSpinBox.setMaximum(maxvalue)
        if self.levelSpinBox.value() < minvalue:
            self.levelSpinBox.setValue(minvalue)
        if self.levelSpinBox.value() > maxvalue:
            self.levelSpinBox.setValue(maxvalue)
        # Update the min and max value spinboxes
        self.minValueSpinBox.setMaximum(maxvalue)
        self.maxValueSpinBox.setMinimum(minvalue)

        # Adjust the levels:
        i = 0
        while self.levelsListView.model().item(i):
            #for i in range(self.levelsListView.model().rowCount()):
            #self.showInfo("Element: " +
            #       str(self.levelsListView.model().item(i).text()))
            #continue
            value = float(self.levelsListView.model().item(i).text())
            if value < minvalue:
                if i == 0:
                    self.levelsListView.model().item(i).setText(str(minvalue))
                    i = i + 1
                else:
                    self.levelsListView.model().removeRow(i)
            elif value > maxvalue:
                if i == self.levelsListView.model().rowCount() - 1:
                    self.levelsListView.model().item(i).setText(str(maxvalue))
                    i = i + 1
                else:
                    self.levelsListView.model().removeRow(i)
            else:
                i = i + 1
        self.drawHistogram()

    def calculateHistogram(self):
        self.showInfo("Calculating histogram...")
        if self.inputlayer is None:
            return

        self.showInfo("Calculating histogram...")
        # Check if there is only one value
        myrange = (self.minValueSpinBox.value(), self.maxValueSpinBox.value())
        self.inputextent = self.inputlayer.extent()
        self.inputrdp = self.inputlayer.dataProvider()
        width = self.inputlayer.width()
        height = self.inputlayer.height()
        if width == 0 or height == 0:
            self.showInfo("Image has zero width or height")
            return
        extwidth = self.inputextent.width()
        extheight = self.inputextent.height()
        # Read the raster block and get the maximum value
        rasterblock = self.inputrdp.block(1, self.inputextent, width, height)
        # Create a numpy array version of the image
        imageMat = np.zeros((height, width), dtype=np.float16)
        # This one takes a lot of time!
        for row in range(height):
            for column in range(width):
                imageMat[row, column] = rasterblock.value(row, column)
                self.showInfo("Image: " + str(height) + ", " + str(width) +
                              " - " + str(imageMat[row, column]))
        self.histo = np.histogram(imageMat, self.histobins, myrange)
        #relevantpixels = imageMat[np.where(imageMat >= bandval)]
        minlevel = float(self.bandMinLabel.text())
        relevantpixels = imageMat[np.where(imageMat >= minlevel)]
        #self.showInfo("Histogram: " + str(self.histo))
        nanpercentage = 100.0 - 100.0 * len(relevantpixels) / (width * height)
        self.bandNANLabel.setText("{0:.1f}".format(nanpercentage))
        #self.showInfo("Percentage NAN: " + str(100.0 - 100.0 *
        #                    len(relevantpixels) / (width * height)))
        #self.showInfo("First element: " + str(self.histo[0]))
        #self.showInfo("First element, first: " + str(self.histo[0][0]))
        #self.showInfo("First element, second: " + str(self.histo[0][1]))
        self.histMinValue.setText(str(self.minValueSpinBox.value()))
        self.histMaxValue.setText(str(self.maxValueSpinBox.value()))
        if self.intband:
            self.histMinValue.setText(str(int(self.minValueSpinBox.value())))
            self.histMaxValue.setText(str(int(self.maxValueSpinBox.value())))
        self.histogramAvailable = True
        self.drawHistogram()

    def drawHistogram(self):
        #if self.inputlayer is None:
        #    return
        self.showInfo("Drawing histogram...")
        viewprect = QRectF(self.histoGraphicsView.viewport().rect())
        self.histoGraphicsView.setSceneRect(viewprect)
        self.setupScene.clear()
        self.setupScene.update()
        histbottom = self.histoGraphicsView.sceneRect().bottom()
        histtop = self.histoGraphicsView.sceneRect().top()
        left = self.histoGraphicsView.sceneRect().left() + self.histopadding
        right = self.histoGraphicsView.sceneRect().right() - self.histopadding
        histheight = histbottom - histtop
        histwidth = right - left
        step = 1.0 * histwidth / self.histobins
        maxlength = histheight
        padding = 1
        ll = QPoint(self.histopadding - 1, histheight - padding)
        start = QPointF(self.histoGraphicsView.mapToScene(ll))

        # Check if there is only one value
        #myrange = (self.minValueSpinBox.value(),self.maxValueSpinBox.value())
        if self.histogramAvailable:
            maxvalue = 0.0
            for i in range(len(self.histo[0])):
                if self.histo[0][i] > maxvalue:
                    maxvalue = self.histo[0][i]
            if maxvalue == 0:
                return
            self.maxBinNumber.setText(str(maxvalue))
            # Create the histogram:
            #self.showInfo("maxvalue: " + str(maxvalue))
            #self.showInfo("maxlength: " + str(maxlength))
            #self.showInfo("step: " + str(step))
            for i in range(self.histobins):
                binnumber = self.histo[0][i]
                if binnumber == 0:
                    continue
                height = (1.0 * self.histo[0][i] / maxvalue *
                          (maxlength - padding))
                rectangle = QGraphicsRectItem(start.x() + step * i, start.y(),
                                              step, -height)
                rectangle.setPen(QPen(QColor(102, 102, 102)))
                rectangle.setBrush(QBrush(QColor(240, 240, 240)))
                self.setupScene.addItem(rectangle)
                #self.showInfo(str(i) + ": " + str(height))
            #if self.levelsListView.model().rowCount() > 0:
        # Add lines for the levels
        minvalue = float(self.histMinValue.text())
        maxvalue = float(self.histMaxValue.text())
        datarange = maxvalue - minvalue
        if datarange == 0:
            return
        i = 0
        while self.levelsListView.model().item(i):
            #self.showInfo("Element: " +
            #       str(self.levelsListView.model().item(i).text()))
            #continue
            value = float(self.levelsListView.model().item(i).text())
            xvalue = start.x() + histwidth * (value - minvalue) / datarange
            line = QGraphicsLineItem(xvalue, 0, xvalue, histheight)
            if i == 0 or i == (self.levelsListView.model().rowCount() - 1):
                line.setPen(QPen(QColor(204, 0, 0)))
            else:
                line.setPen(QPen(QColor(0, 204, 0)))
            self.setupScene.addItem(line)
            i = i + 1

    def suggestLevels(self):
        self.listModel.clear()
        self.showInfo("Suggesting levels")
        levels = self.levelsSpinBox.value()
        startvalue = self.minValueSpinBox.value()
        endvalue = self.maxValueSpinBox.value()
        increment = (endvalue - startvalue) / levels
        for i in range(levels + 1):
            value = startvalue + increment * i
            if self.intband:
                value = int(value)
            item = QStandardItem(str(value))
            self.listModel.appendRow(item)
        self.drawHistogram()

    def addLevel(self):
        newvalue = self.levelSpinBox.value()
        if self.intband:
            newvalue = int(newvalue)
        for i in range(self.listModel.rowCount()):
            # Check if the value is already in the list
            if self.listModel.item(i).text() == str(newvalue):
                return
            else:
                # Maintain a sorted list of distances
                if (float(self.listModel.item(i).text()) > float(
                        str(newvalue))):
                    item = QStandardItem(str(newvalue))
                    self.listModel.insertRow(i, item)
                    self.drawHistogram()
                    return
        item = QStandardItem(str(newvalue))
        self.listModel.appendRow(item)
        #if self.histogramAvailable:
        #    addLevelsToHistogram()
        self.drawHistogram()

    def removeLevel(self):
        self.levelsListView.setUpdatesEnabled(False)
        indexes = self.levelsListView.selectedIndexes()
        indexes.sort()
        for i in range(len(indexes) - 1, -1, -1):
            self.listModel.removeRow(indexes[i].row())
        self.levelsListView.setUpdatesEnabled(True)
        #if self.histogramAvailable:
        #    removeLevelFromHistogram()
        self.drawHistogram()

    def layerlistchanged(self):
        self.layerlistchanging = True
        self.showInfo("Layer list changed")
        # Repopulate the input layer combo box
        # Save the currently selected input layer
        inputlayerid = self.inputlayerid
        self.inputRaster.clear()
        for alayer in self.iface.legendInterface().layers():
            if alayer.type() == QgsMapLayer.RasterLayer:
                gdalmetadata = alayer.metadata()
                # Skip WMS layers
                WMSstring = 'Web Map Service'
                wmspos = gdalmetadata.find(WMSstring)
                if wmspos != -1:
                    continue
                self.inputRaster.addItem(alayer.name(), alayer.id())
        # Set the previous selection
        for i in range(self.inputRaster.count()):
            if self.inputRaster.itemData(i) == inputlayerid:
                self.inputRaster.setCurrentIndex(i)
        self.layerlistchanging = False
        #self.updateui()

    def updateui(self):
        """Do the necessary updates after a layer selection has
           been changed."""
        #if self.layerlistchanged:
        #    return
        #self.outputRaster.setText(self.inputRaster.currentText() +
        #                           '_' + 'thinned')
        layerindex = self.inputRaster.currentIndex()
        layerId = self.inputRaster.itemData(layerindex)
        #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
        inputlayer = QgsProject.instance().mapLayer(layerId)
        if inputlayer is not None:
            pass
        else:
            pass

    def findGdalDatatype(self, shortdesc):
        gdaldatatype = None
        # // Unknown or unspecified type
        # GDT_Unknown = GDALDataType(C.GDT_Unknown)
        if shortdesc == 'Unknown':
            gdaldatatype = gdal.GDT_Unknown
        # // Eight bit unsigned integer
        # GDT_Byte = GDALDataType(C.GDT_Byte)
        elif shortdesc == 'Byte':
            gdaldatatype = gdal.GDT_Byte
        # // Sixteen bit unsigned integer
        # GDT_UInt16 = GDALDataType(C.GDT_UInt16)
        elif shortdesc == 'UInt16':
            gdaldatatype = gdal.GDT_UInt16
        # // Sixteen bit signed integer
        # GDT_Int16 = GDALDataType(C.GDT_Int16)
        elif shortdesc == 'Int16':
            gdaldatatype = gdal.GDT_Int16
        # // Thirty two bit unsigned integer
        # GDT_UInt32 = GDALDataType(C.GDT_UInt32)
        elif shortdesc == 'UInt32':
            gdaldatatype = gdal.GDT_UInt32
        # // Thirty two bit signed integer
        # GDT_Int32 = GDALDataType(C.GDT_Int32)
        elif shortdesc == 'Int32':
            gdaldatatype = gdal.GDT_Int32
        # // Thirty two bit floating point
        # GDT_Float32 = GDALDataType(C.GDT_Float32)
        elif shortdesc == 'Float32':
            gdaldatatype = gdal.GDT_Float32
        # // Sixty four bit floating point
        # GDT_Float64 = GDALDataType(C.GDT_Float64)
        elif shortdesc == 'Float64':
            gdaldatatype = gdal.GDT_Float64
        # // Complex Int16
        # GDT_CInt16 = GDALDataType(C.GDT_CInt16)
        elif shortdesc == 'CInt16':
            gdaldatatype = gdal.CInt16
        # // Complex Int32
        # GDT_CInt32 = GDALDataType(C.GDT_CInt32)
        elif shortdesc == 'CInt32':
            gdaldatatype = gdal.CInt32
        # // Complex Float32
        # GDT_CFloat32 = GDALDataType(C.GDT_CFloat32)
        elif shortdesc == 'CFloat32':
            gdaldatatype = gdal.CFloat32
        # // Complex Float64
        # GDT_CFloat64 = GDALDataType(C.GDT_CFloat64)
        elif shortdesc == 'CFloat64':
            gdaldatatype = gdal.CFloat64
        # // maximum type # + 1
        # GDT_TypeCount = GDALDataType(C.GDT_TypeCount)
        elif shortdesc == 'TypeCount':
            gdaldatatype = gdal.TypeCount
        self.gdaldatatype = gdaldatatype

    def killWorker(self):
        """Kill the worker thread."""
        if self.worker is not None:
            QgsMessageLog.logMessage(self.tr('Killing worker'),
                                     self.THINGREYSCALE, Qgis.Info)
            self.worker.kill()

    def showError(self, text):
        """Show an error."""
        self.iface.messageBar().pushMessage(self.tr('Error'),
                                            text,
                                            level=QgsMessageBar.CRITICAL,
                                            duration=3)
        QgsMessageLog.logMessage('Error: ' + text, self.THINGREYSCALE,
                                 QgsMessageLog.CRITICAL)

    def showWarning(self, text):
        """Show a warning."""
        self.iface.messageBar().pushMessage(self.tr('Warning'),
                                            text,
                                            level=QgsMessageBar.WARNING,
                                            duration=2)
        QgsMessageLog.logMessage('Warning: ' + text, self.THINGREYSCALE,
                                 QgsMessageLog.WARNING)

    def showInfo(self, text):
        """Show info."""
        self.iface.messageBar().pushMessage(self.tr('Info'),
                                            text,
                                            level=Qgis.Info,
                                            duration=2)
        QgsMessageLog.logMessage('Info: ' + text, self.THINGREYSCALE,
                                 Qgis.Info)

    # def help(self):
    # #QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir +
    #                                 "/help/build/html/index.html"))
    # QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir +
    #                                            "/help/index.html"))
    # #showPluginHelp()

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        return QCoreApplication.translate('ThinGreyScaleDialog', message)

    def browse(self):
        settings = QSettings()
        key = '/UI/lastShapefileDir'
        outDir = settings.value(key)
        home = outDir
        #outDir = expanduser("~")
        #filter = (self.DEFAULTPROVIDER + " (*" +
        #             self.DEFAULTEXTENSION + ");;All files (*)")
        filter = (self.DEFAULTPROVIDER + " (*" + self.DEFAULTEXTENSION +
                  self.EXTRAEXTENSION + ")")
        #if (self.gdalprovider != self.DEFAULTPROVIDER and
        #                                     (self.canCreateCopy or
        #                                           self.canCreate)):
        #    filter = (self.gdalprovider + " (*" + self.gdalext +
        #                                          ");;" + filter)
        outFilePath = QFileDialog.getSaveFileName(
            self, 'Specify file name for skeleton', outDir, filter)
        outFilePath = unicode(outFilePath)
        if outFilePath:
            root, ext = splitext(outFilePath)
            if ext.lower() != '.tif' and ext.lower() != '.tiff':
                outFilePath = '%s.tif' % outFilePath
            outDir = dirname(outFilePath)
            settings.setValue(key, outDir)
        #        (self.canCreateCopy or self.canCreate):
        #    fileName = splitext(str(fileName))[0]+self.gdalext
        self.outputRaster.setText(outFilePath)

    # Overriding
    def resizeEvent(self, event):
        #self.showInfo("resizeEvent")
        self.calculateHistogram()

    def help(self):
        #QDesktopServices.openUrl(QUrl.fromLocalFile(
        #                 self.plugin_dir + "/help/html/index.html"))
        showPluginHelp(None, "help/html/index")

    # Implement the accept method to avoid exiting the dialog when
    # starting the work
    def accept(self):
        """Accept override."""
        pass

    # Implement the reject method to have the possibility to avoid
    # exiting the dialog when cancelling
    def reject(self):
        """Reject override."""
        # exit the dialog
        QDialog.reject(self)
Example #5
0
class MultipleInputDialog(BASE, WIDGET):

    def __init__(self, options, selectedoptions=None, datatype=None):
        super(MultipleInputDialog, self).__init__(None)
        self.setupUi(self)
        self.datatype = datatype
        self.model = None

        self.options = []
        for i, option in enumerate(options):
            if option is None or isinstance(option, str):
                self.options.append((i, option))
            else:
                self.options.append((option[0], option[1]))

        self.selectedoptions = selectedoptions or []

        # Additional buttons
        self.btnSelectAll = QPushButton(self.tr('Select All'))
        self.buttonBox.addButton(self.btnSelectAll,
                                 QDialogButtonBox.ActionRole)
        self.btnClearSelection = QPushButton(self.tr('Clear Selection'))
        self.buttonBox.addButton(self.btnClearSelection,
                                 QDialogButtonBox.ActionRole)
        self.btnToggleSelection = QPushButton(self.tr('Toggle Selection'))
        self.buttonBox.addButton(self.btnToggleSelection,
                                 QDialogButtonBox.ActionRole)
        if self.datatype is not None:
            btnAddFile = QPushButton(QCoreApplication.translate("MultipleInputDialog", 'Add File(s)…'))
            btnAddFile.clicked.connect(self.addFiles)
            self.buttonBox.addButton(btnAddFile,
                                     QDialogButtonBox.ActionRole)

        self.btnSelectAll.clicked.connect(lambda: self.selectAll(True))
        self.btnClearSelection.clicked.connect(lambda: self.selectAll(False))
        self.btnToggleSelection.clicked.connect(self.toggleSelection)

        self.settings = QgsSettings()
        self.restoreGeometry(self.settings.value("/Processing/multipleInputDialogGeometry", QByteArray()))

        self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.lstLayers.setDragDropMode(QAbstractItemView.InternalMove)

        self.populateList()
        self.finished.connect(self.saveWindowGeometry)

    def saveWindowGeometry(self):
        self.settings.setValue("/Processing/multipleInputDialogGeometry", self.saveGeometry())

    def populateList(self):
        self.model = QStandardItemModel()
        for value, text in self.options:
            item = QStandardItem(text)
            item.setData(value, Qt.UserRole)
            item.setCheckState(Qt.Checked if value in self.selectedoptions else Qt.Unchecked)
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        # add extra options (e.g. manually added layers)
        for t in [o for o in self.selectedoptions if not isinstance(o, int)]:
            if isinstance(t, QgsProcessingModelChildParameterSource):
                item = QStandardItem(t.staticValue())
            else:
                item = QStandardItem(t)
            item.setData(item.text(), Qt.UserRole)
            item.setCheckState(Qt.Checked)
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        self.lstLayers.setModel(self.model)

    def accept(self):
        self.selectedoptions = []
        model = self.lstLayers.model()
        for i in range(model.rowCount()):
            item = model.item(i)
            if item.checkState() == Qt.Checked:
                self.selectedoptions.append(item.data(Qt.UserRole))
        QDialog.accept(self)

    def reject(self):
        self.selectedoptions = None
        QDialog.reject(self)

    def getItemsToModify(self):
        items = []
        if len(self.lstLayers.selectedIndexes()) > 1:
            for i in self.lstLayers.selectedIndexes():
                items.append(self.model.itemFromIndex(i))
        else:
            for i in range(self.model.rowCount()):
                items.append(self.model.item(i))
        return items

    def selectAll(self, value):
        for item in self.getItemsToModify():
            item.setCheckState(Qt.Checked if value else Qt.Unchecked)

    def toggleSelection(self):
        for item in self.getItemsToModify():
            checked = item.checkState() == Qt.Checked
            item.setCheckState(Qt.Unchecked if checked else Qt.Checked)

    def getFileFilter(self, datatype):
        """
        Returns a suitable file filter pattern for the specified parameter definition
        :param param:
        :return:
        """
        if datatype == QgsProcessing.TypeRaster:
            return QgsProviderRegistry.instance().fileRasterFilters()
        elif datatype == QgsProcessing.TypeFile:
            return self.tr('All files (*.*)')
        else:
            exts = QgsVectorFileWriter.supportedFormatExtensions()
            for i in range(len(exts)):
                exts[i] = self.tr('{0} files (*.{1})').format(exts[i].upper(), exts[i].lower())
            return self.tr('All files (*.*)') + ';;' + ';;'.join(exts)

    def addFiles(self):
        filter = self.getFileFilter(self.datatype)

        settings = QgsSettings()
        path = str(settings.value('/Processing/LastInputPath'))

        ret, selected_filter = QFileDialog.getOpenFileNames(self, self.tr('Select File(s)'),
                                                            path, filter)
        if ret:
            files = list(ret)
            settings.setValue('/Processing/LastInputPath',
                              os.path.dirname(str(files[0])))
            for filename in files:
                item = QStandardItem(filename)
                item.setData(filename, Qt.UserRole)
                item.setCheckState(Qt.Checked)
                item.setCheckable(True)
                item.setDropEnabled(False)
                self.model.appendRow(item)
Example #6
0
class QRAVEDockWidget(QDockWidget, Ui_QRAVEDockWidgetBase):

    closingPlugin = pyqtSignal()
    dataChange = pyqtSignal()
    showMeta = pyqtSignal()
    metaChange = pyqtSignal(str, str, dict, bool)

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

        self.setupUi(self)
        self.menu = ContextMenu()
        self.qproject = QgsProject.instance()
        self.qproject.cleared.connect(self.close_all)

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(self.open_menu)
        self.treeView.doubleClicked.connect(self.default_tree_action)
        self.treeView.clicked.connect(self.item_change)

        self.treeView.expanded.connect(self.expand_tree_item)

        self.settings = Settings()

        self.model = QStandardItemModel()

        self.loaded_projects: List(Project) = []

        # Initialize our classes
        self.basemaps = BaseMaps()
        self.treeView.setModel(self.model)

        self.dataChange.connect(self.reload_tree)
        self.reload_tree()

    def expand_tree_item(self, idx: QModelIndex):
        item = self.model.itemFromIndex(idx)
        item_data = item.data(Qt.UserRole)
        if item_data and item_data.data and isinstance(item_data.data, QRaveBaseMap):
            item_data.data.load_layers()

    def _get_project(self, xml_path):
        try:
            return next(iter(self.loaded_projects))
        except Exception:
            return None

    @pyqtSlot()
    def reload_tree(self):
        # re-initialize our model and reload the projects from file
        # Try not to do this too often if you can
        self.model.clear()
        self.loaded_projects = []
        qrave_projects = self.get_project_settings()

        for project_path in qrave_projects:
            project = Project(project_path)
            project.load()

            if project is not None and project.exists is True and project.qproject is not None:
                self.model.appendRow(project.qproject)
                self.expand_children_recursive(self.model.indexFromItem(project.qproject))
                self.loaded_projects.append(project)

        # Load the tree objects
        self.basemaps.load()

        # Now load the basemaps
        region = self.settings.getValue('basemapRegion')
        if self.settings.getValue('basemapsInclude') is True \
                and region is not None and len(region) > 0 \
                and region in self.basemaps.regions.keys():
            self.model.appendRow(self.basemaps.regions[region])
            self.expand_children_recursive(self.model.indexFromItem(self.basemaps.regions[region]))

    def get_project_settings(self):
        try:
            qrave_projects_raw, type_conversion_ok = self.qproject.readEntry(
                CONSTANTS['settingsCategory'],
                'qrave_projects'
            )

            if type_conversion_ok is False:
                qrave_projects = []
            else:
                qrave_projects = json.loads(qrave_projects_raw)

                if qrave_projects is None or not isinstance(qrave_projects, list):
                    qrave_projects = []

        except Exception as e:
            self.settings.log('Error loading project settings: {}'.format(e), Qgis.Warning)
            qrave_projects = []

        filtered = [pf for pf in qrave_projects if os.path.isfile(pf)]
        filtered.reverse()
        # We Treat this like a stack where the last project in goes on the top.
        # Element 0 should be the top item
        return filtered

    def set_project_settings(self, projects: List[str]):
        self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'qrave_projects', json.dumps(projects))

    @pyqtSlot()
    def add_project(self, xml_path: str):
        qrave_projects = self.get_project_settings()

        # If this project is not already in
        if xml_path not in qrave_projects:
            qrave_projects.append(xml_path)
            self.set_project_settings(qrave_projects)
            self.reload_tree()

            new_project = self._get_project(xml_path)

            # If this is a fresh load and the setting is set we load the default view
            load_default_setting = self.settings.getValue('loadDefaultView')

            if load_default_setting is True \
                    and new_project.default_view is not None \
                    and new_project.default_view in new_project.views:
                self.add_children_to_map(new_project.qproject, new_project.views[new_project.default_view])

    def closeEvent(self, event):
        """ When the user clicks the "X" in the dockwidget titlebar
        """
        self.hide()
        self.qproject.removeEntry(CONSTANTS['settingsCategory'], 'enabled')
        self.closingPlugin.emit()
        event.accept()

    def expand_children_recursive(self, idx: QModelIndex = None, force=False):
        """Expand all the children of a QTreeView node. Do it recursively
        TODO: Recursion might not be the best for large trees here.

        Args:
            idx (QModelIndex, optional): [description]. Defaults to None.
            force: ignore the "collapsed" business logic attribute
        """
        if idx is None:
            idx = self.treeView.rootIndex()

        for idy in range(self.model.rowCount(idx)):
            child = self.model.index(idy, 0, idx)
            self.expand_children_recursive(child, force)

        item = self.model.itemFromIndex(idx)
        item_data = item.data(Qt.UserRole) if item is not None else None

        # NOTE: This is pretty verbose on purpose

        # This thing needs to have data or it defaults to being expanded
        if item_data is None or item_data.data is None:
            collapsed = False

        # Collapsed is an attribute set in the business logic
        # Never expand the QRaveBaseMap object becsause there's a network call involved
        elif isinstance(item_data.data, QRaveBaseMap) \
                or (isinstance(item_data.data, dict) and 'collapsed' in item_data.data and item_data.data['collapsed'] == 'true'):
            collapsed = True

        else:
            collapsed = False

        if not self.treeView.isExpanded(idx) and not collapsed:
            self.treeView.setExpanded(idx, True)

    def default_tree_action(self, idx: QModelIndex):
        if not idx.isValid():
            return

        item = self.model.itemFromIndex(idx)
        item_data: ProjectTreeData = item.data(Qt.UserRole)

        # This is the default action for all add-able layers including basemaps
        if isinstance(item_data.data, QRaveMapLayer):
            if item_data.data.layer_type in [QRaveMapLayer.LayerTypes.FILE, QRaveMapLayer.LayerTypes.REPORT]:
                self.file_system_open(item_data.data.layer_uri)
            else:
                QRaveMapLayer.add_layer_to_map(item)

        # Expand is the default option for wms because we might need to load the layers
        elif isinstance(item_data.data, QRaveBaseMap):
            if item_data.data.tile_type == 'wms':
                pass
            # All the XYZ layers can be added normally.
            else:
                QRaveMapLayer.add_layer_to_map(item)

        elif item_data.type in [QRaveTreeTypes.PROJECT_ROOT]:
            self.change_meta(item, item_data, True)

        # For folder-y types we want Expand and contract is already implemented as a default
        elif item_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            # print("Default Folder Action")
            pass

        elif item_data.type == QRaveTreeTypes.PROJECT_VIEW:
            print("Default View Action")
            self.add_view_to_map(item_data)

    def item_change(self, pos):
        """Triggered when the user selects a new item in the tree

        Args:pos
            pos ([type]): [description]
        """
        indexes = self.treeView.selectedIndexes()

        # No multiselect so there is only ever one item
        item = self.model.itemFromIndex(indexes[0])
        data_item: ProjectTreeData = item.data(Qt.UserRole)

        if len(indexes) < 1 or data_item.project is None or not data_item.project.exists:
            return

        # Update the metadata if we need to
        self.change_meta(item, data_item)

    def change_meta(self, item: QStandardItem, item_data: ProjectTreeData, show=False):
        """Update the MetaData dock widget with new information

        Args:
            item (QStandardItem): [description]
            data ([type]): [description]
            show (bool, optional): [description]. Defaults to False.
        """
        data = item_data.data
        if isinstance(data, QRaveMapLayer):
            meta = data.meta if data.meta is not None else {}
            self.metaChange.emit(item.text(), MetaType.LAYER, meta, show)

        elif isinstance(data, QRaveBaseMap):
            self.metaChange.emit(item.text(), MetaType.NONE, {}, show)

        elif item_data.type == QRaveTreeTypes.PROJECT_ROOT:
            self.metaChange.emit(item.text(), MetaType.PROJECT, {
                'project': item_data.project.meta,
                'warehouse': item_data.project.warehouse_meta
            }, show)
        elif item_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            self.metaChange.emit(item.text(), MetaType.FOLDER, data or {}, show)
        elif isinstance(data, dict):
            # this is just the generic case for any kind of metadata
            self.metaChange.emit(item.text(), MetaType.NONE, data or {}, show)
        else:
            # Do not  update the metadata if we have nothing to show
            self.metaChange.emit(item.text(), MetaType.NONE, {}, show)

    def get_warehouse_url(self, wh_meta: Dict[str, str]):

        if wh_meta is not None:

            if 'program' in wh_meta and 'id' in wh_meta:
                return '/'.join([CONSTANTS['warehouseUrl'], wh_meta['program'], wh_meta['id']])

            elif '_rs_wh_id' in wh_meta and '_rs_wh_program' in wh_meta:
                return '/'.join([CONSTANTS['warehouseUrl'], wh_meta['_rs_wh_program'], wh_meta['_rs_wh_id']])

        return None

    def project_warehouse_view(self, project: Project):
        """Open this project in the warehouse if the warehouse meta entries exist
        """
        url = self.get_warehouse_url(project.warehouse_meta)
        if url is not None:
            QDesktopServices.openUrl(QUrl(url))

    def layer_warehouse_view(self, data: QRaveMapLayer):
        """Open this project in the warehouse if the warehouse meta entries exist
        """
        url = self.get_warehouse_url(data.meta)
        if url is not None:
            QDesktopServices.openUrl(QUrl(url))

    def close_all(self):
        for p in range(len(self.loaded_projects)):
            self.close_project(self.loaded_projects[0])

    def close_project(self, project: Project):
        """ Close the project
        """
        try:
            qrave_projects_raw, type_conversion_ok = self.qproject.readEntry(
                CONSTANTS['settingsCategory'],
                'qrave_projects'
            )
            qrave_projects = json.loads(qrave_projects_raw)
            if not type_conversion_ok or qrave_projects is None:
                qrave_projects = []
        except Exception as e:
            self.settings.log('Error closing project: {}'.format(e), Qgis.Warning)
            qrave_projects = []

        # Filter out the project we want to close and reload the tree
        qrave_projects = [x.project_xml_path for x in self.loaded_projects if x != project]

        # Write the settings back to the project
        self.qproject.writeEntry(CONSTANTS['settingsCategory'], 'qrave_projects', json.dumps(qrave_projects))
        self.loaded_projects = qrave_projects
        self.reload_tree()

    def file_system_open(self, fpath: str):
        """Open a file on the operating system using the default action

        Args:
            fpath (str): [description]
        """
        qurl = QUrl.fromLocalFile(fpath)
        QDesktopServices.openUrl(QUrl(qurl))

    def file_system_locate(self, fpath: str):
        """This the OS-agnostic "show in Finder" or "show in explorer" equivalent
        It should open the folder of the item in question

        Args:
            fpath (str): [description]
        """
        final_path = os.path.dirname(fpath)
        while not os.path.isdir(final_path):
            final_path = os.path.dirname(final_path)

        qurl = QUrl.fromLocalFile(final_path)
        QDesktopServices.openUrl(qurl)

    def toggleSubtree(self, item: QStandardItem = None, expand=True):

        def _recurse(curritem):
            idx = self.model.indexFromItem(item)
            if expand is not self.treeView.isExpanded(idx):
                self.treeView.setExpanded(idx, expand)

            for row in range(curritem.rowCount()):
                _recurse(curritem.child(row))

        if item is None:
            if expand is True:
                self.treeView.expandAll()
            else:
                self.treeView.collapseAll()
        else:
            _recurse(item)

    def add_view_to_map(self, item_data: ProjectTreeData):
        """Add a view and all its layers to the map

        Args:
            item (QStandardItem): [description]
        """
        self.add_children_to_map(item_data.project.qproject, item_data.data)

    def add_children_to_map(self, item: QStandardItem, bl_ids: List[str] = None):
        """Iteratively add all children to the map

        Args:
            item (QStandardItem): [description]
            bl_ids (List[str], optional): List of ids to filter by so we don't load everything. this is used for loading views
        """

        for child in self._get_children(item):
            # Is this something we can add to the map?
            project_tree_data = child.data(Qt.UserRole)
            if project_tree_data is not None and isinstance(project_tree_data.data, QRaveMapLayer):
                data = project_tree_data.data
                loadme = False
                # If this layer matches the businesslogic id filter
                if bl_ids is not None and len(bl_ids) > 0:
                    if 'id' in data.bl_attr and data.bl_attr['id'] in bl_ids:
                        loadme = True
                else:
                    loadme = True

                if loadme is True:
                    data.add_layer_to_map(child)

    def _get_children(self, root_item: QStandardItem):
        """Recursion is going to kill us here so do an iterative solution instead
           https://stackoverflow.com/questions/41949370/collect-all-items-in-qtreeview-recursively

        Yields:
            [type]: [description]
        """
        stack = [root_item]
        while stack:
            parent = stack.pop(0)
            for row in range(parent.rowCount()):
                for column in range(parent.columnCount()):
                    child = parent.child(row, column)
                    yield child
                    if child.hasChildren():
                        stack.append(child)

    def _get_parents(self, start_item: QStandardItem):
        stack = []
        placeholder = start_item.parent()
        while placeholder is not None and placeholder != self.model.invisibleRootItem():
            stack.append(placeholder)
            placeholder = start_item.parent()

        return stack.reverse()

    def open_menu(self, position):

        indexes = self.treeView.selectedIndexes()
        if len(indexes) < 1:
            return

        # No multiselect so there is only ever one item
        idx = indexes[0]

        if not idx.isValid():
            return

        item = self.model.itemFromIndex(indexes[0])
        project_tree_data = item.data(Qt.UserRole)  # ProjectTreeData object
        data = project_tree_data.data  # Could be a QRaveBaseMap, a QRaveMapLayer or just some random data

        # This is the layer context menu
        if isinstance(data, QRaveMapLayer):
            if data.layer_type == QRaveMapLayer.LayerTypes.WEBTILE:
                self.basemap_context_menu(idx, item, project_tree_data)
            elif data.layer_type in [QRaveMapLayer.LayerTypes.FILE, QRaveMapLayer.LayerTypes.REPORT]:
                self.file_layer_context_menu(idx, item, project_tree_data)
            else:
                self.map_layer_context_menu(idx, item, project_tree_data)

        elif isinstance(data, QRaveBaseMap):
            # A WMS QARaveBaseMap is just a container for layers
            if data.tile_type == 'wms':
                self.folder_dumb_context_menu(idx, item, project_tree_data)
            # Every other kind of basemap is an add-able layer
            else:
                self.basemap_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type == QRaveTreeTypes.PROJECT_ROOT:
            self.project_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type in [
            QRaveTreeTypes.PROJECT_VIEW_FOLDER,
            QRaveTreeTypes.BASEMAP_ROOT,
            QRaveTreeTypes.BASEMAP_SUPER_FOLDER
        ]:
            self.folder_dumb_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type in [
            QRaveTreeTypes.PROJECT_FOLDER,
            QRaveTreeTypes.PROJECT_REPEATER_FOLDER,
            QRaveTreeTypes.BASEMAP_SUB_FOLDER
        ]:
            self.folder_context_menu(idx, item, project_tree_data)

        elif project_tree_data.type == QRaveTreeTypes.PROJECT_VIEW:
            self.view_context_menu(idx, item, project_tree_data)

        self.menu.exec_(self.treeView.viewport().mapToGlobal(position))

    def map_layer_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_TO_MAP', lambda: QRaveMapLayer.add_layer_to_map(item), enabled=item_data.data.exists)
        self.menu.addAction('VIEW_LAYER_META', lambda: self.change_meta(item, item_data, True))

        if bool(self.get_warehouse_url(item_data.data.meta)):
            self.menu.addAction('VIEW_WEB_SOURCE', lambda: self.layer_warehouse_view(item_data))

        self.menu.addAction('BROWSE_FOLDER', lambda: self.file_system_locate(item_data.data.layer_uri))

    def file_layer_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('OPEN_FILE', lambda: self.file_system_open(item_data.data.layer_uri))
        self.menu.addAction('BROWSE_FOLDER', lambda: self.file_system_locate(item_data.data.layer_uri))

    # Basemap context items
    def basemap_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_TO_MAP', lambda: QRaveMapLayer.add_layer_to_map(item))

    # Folder-level context menu
    def folder_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_children_to_map(item))
        self.menu.addSeparator()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(item, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(item, True))

    # Some folders don't have the 'ADD_ALL_TO_MAP' functionality enabled
    def folder_dumb_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(item, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(item, True))

    # View context items
    def view_context_menu(self, idx: QModelIndex, item: QStandardItem, item_data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_view_to_map(item_data))

    # Project-level context menu
    def project_context_menu(self, idx: QModelIndex, item: QStandardItem, data: ProjectTreeData):
        self.menu.clear()
        self.menu.addAction('COLLAPSE_ALL', lambda: self.toggleSubtree(None, False))
        self.menu.addAction('EXPAND_ALL', lambda: self.toggleSubtree(None, True))

        self.menu.addSeparator()
        self.menu.addAction('BROWSE_PROJECT_FOLDER', lambda: self.file_system_locate(data.project.project_xml_path))
        self.menu.addAction('VIEW_PROJECT_META', lambda: self.change_meta(item, data, True))
        self.menu.addAction('WAREHOUSE_VIEW', lambda: self.project_warehouse_view(data.project), enabled=bool(self.get_warehouse_url(data.project.warehouse_meta)))
        self.menu.addAction('ADD_ALL_TO_MAP', lambda: self.add_children_to_map(item))
        self.menu.addSeparator()
        self.menu.addAction('REFRESH_PROJECT_HIERARCHY', self.reload_tree)
        self.menu.addAction('CUSTOMIZE_PROJECT_HIERARCHY', enabled=False)
        self.menu.addSeparator()
        self.menu.addAction('CLOSE_PROJECT', lambda: self.close_project(data.project), enabled=bool(data.project))
Example #7
0
class MultipleSelectTreeView(QListView):
    """
    Custom QListView implementation that displays checkable items from a
    multiple select column type.
    """
    def __init__(self, column, parent=None):
        """
        Class constructor.
        :param column: Multiple select column object.
        :type column: MultipleSelectColumn
        :param parent: Parent widget for the control.
        :type parent: QWidget
        """
        QListView.__init__(self, parent)

        # Disable editing of lookup values
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.column = column

        self._item_model = QStandardItemModel(self)

        self._value_list = self.column.value_list

        # Stores lookup objects based on primary keys
        self._lookup_cache = {}

        self._initialize()

        self._association = self.column.association

        self._first_parent_col = self._association.first_reference_column.name
        self._second_parent_col = self._association.second_reference_column.name

        # Association model
        self._assoc_cls = entity_model(self._association)

    def reset_model(self):
        """
        Resets the item model.
        """
        self._item_model.clear()
        self._item_model.setColumnCount(2)

    def clear(self):
        """
        Clears all items in the model.
        """
        self._item_model.clear()

    @property
    def association(self):
        """
        :return: Returns the association object corresponding to the column.
        :rtype: AssociationEntity
        """
        return self._association

    @property
    def value_list(self):
        """
        :return: Returns the ValueList object corresponding to the configured
        column object.
        :rtype: ValueList
        """
        return self._value_list

    @property
    def item_model(self):
        """
        :return: Returns the model corresponding to the checkable items.
        :rtype: QStandardItemModel
        """
        return self._item_model

    def _add_item(self, id, value):
        """
        Adds a row corresponding to id and corresponding value from a lookup
        table.
        :param id: Primary key of a lookup record.
        :type id: int
        :param value: Lookup value
        :type value: str
        """
        value_item = QStandardItem(value)
        value_item.setCheckable(True)
        id_item = QStandardItem(str(id))

        self._item_model.appendRow([value_item, id_item])

    def _initialize(self):
        # Populate list with lookup items
        self.reset_model()

        # Add all lookup values in the value list table
        vl_cls = entity_model(self._value_list)
        if not vl_cls is None:
            vl_obj = vl_cls()
            res = vl_obj.queryObject().all()
            for r in res:
                self._lookup_cache[r.id] = r
                self._add_item(r.id, r.value)

        self.setModel(self._item_model)

    def clear_selection(self):
        """
        Unchecks all items in the view.
        """
        for i in range(self._item_model.rowCount()):
            value_item = self._item_model.item(i, 0)

            if value_item.checkState() == Qt.Checked:
                value_item.setCheckState(Qt.Unchecked)

                if value_item.rowCount() > 0:
                    value_item.removeRow(0)

    def selection(self):
        """
        :return: Returns a list of selected items.
        :rtype: list
        """
        selection = []

        for i in range(self._item_model.rowCount()):
            value_item = self._item_model.item(i, 0)

            if value_item.checkState() == Qt.Checked:
                id_item = self._item_model.item(i, 1)
                id = int(id_item.text())

                # Get item from the lookup cache and append to selection
                if id in self._lookup_cache:
                    lookup_rec = self._lookup_cache[id]
                    selection.append(lookup_rec)

        return selection

    def set_selection(self, models):
        """
        Checks items corresponding to the specified models.
        :param models: List containing model values in the view for selection.
        :type models: list
        """
        for m in models:
            search_value = m.value
            v_items = self._item_model.findItems(search_value)

            # Loop through result and check items
            for vi in v_items:
                if vi.checkState() == Qt.Unchecked:
                    vi.setCheckState(Qt.Checked)
Example #8
0
class DialListCheckBox(object):
    """
    Dialog with list check box
    example : 1,2,4,6,8
    """
    def __init__(self, values, checked_values=[]):
        self.dial = QDialog()
        self.result = ""

        # Layout Principal
        m_vbl = QVBoxLayout(self.dial)

        # List item checkable
        view = QListView()
        m_vbl.addWidget(view)

        self.m_sim = QStandardItemModel()
        view.setModel(self.m_sim)

        self._load_item(values, checked_values)

        # List buttons
        bt_all = QPushButton(self.tr("All"))
        bt_all.clicked.connect(lambda: self._check_all())

        bt_nothing = QPushButton(self.tr("Nothing"))
        bt_nothing.clicked.connect(lambda: self._check_nothing())

        bt_print = QPushButton(self.tr("Ok"))
        bt_print.clicked.connect(lambda: self._check_ok())

        # Sub layout for buttons
        m_vbh = QHBoxLayout()
        m_vbl.addLayout(m_vbh)

        m_vbh.addWidget(bt_all)
        m_vbh.addWidget(bt_nothing)
        m_vbh.addWidget(bt_print)

    def _load_item(self, values, checked_values):
        """Load item list"""
        for v in values:
            item = QStandardItem(str(v))
            if v in checked_values:
                item.setCheckState(Qt.Checked)
            else:
                item.setCheckState(Qt.Unchecked)
            item.setCheckable(True)
            self.m_sim.appendRow(item)

    def _check_all(self):
        """Check all item"""
        for index in range(self.m_sim.rowCount(
        )):  ## Iteration sur le model (contient la list des items)
            item = self.m_sim.item(index)
            if item.isCheckable() and item.checkState(
            ) == Qt.Unchecked:  # Si Unchecked
                item.setCheckState(Qt.Checked)

    def _check_nothing(self):
        """Uncheck all item"""
        for index in range(self.m_sim.rowCount(
        )):  ## Iteration sur le model (contient la list des items)
            item = self.m_sim.item(index)
            if item.isCheckable() and item.checkState(
            ) == Qt.Checked:  # Si Checked
                item.setCheckState(Qt.Unchecked)

    def _check_ok(self):
        """Get value of checked items and exit"""
        l_value = []
        for index in range(self.m_sim.rowCount(
        )):  ## Iteration sur le model (contient la list des items)
            item = self.m_sim.item(index)
            if item.isCheckable() and item.checkState(
            ) == Qt.Checked:  # Si Checked
                l_value.append(str(item.text()))

        if l_value:
            s_value = ";".join(l_value)
        else:
            s_value = ""

        self.result = s_value
        self.dial.close()

    def run(self):

        self.dial.setWindowTitle(self.tr("Select values"))

        self.dial.exec_()
        return self.result

    def tr(self, string, context=''):
        if context == '' or context == None:
            context = self.__class__.__name__
        return QCoreApplication.translate(context, string)
class DialogImportData(QDialog, DIALOG_UI):
    def __init__(self, iface, db, qgis_utils):
        QDialog.__init__(self)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.db = db
        self.qgis_utils = qgis_utils
        self.base_configuration = BaseConfiguration()

        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._conf_db = ConfigDbSupported()
        self._params = None
        self._current_db = None

        self.xtf_file_browse_button.clicked.connect(
            make_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate(
                    "DialogImportData", 'Open Transfer or Catalog File'),
                file_filter=QCoreApplication.translate(
                    "DialogImportData",
                    'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)'
                )))

        self.validators = Validators()
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogImportData",
                                       "[Name of the XTF to be created]"))
        fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml'])
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(self.update_import_models)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        # db
        self.connection_setting_button.clicked.connect(self.show_settings)

        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogImportData",
                                       'Connection Settings'))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogImportData", "Show log"))

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            QCoreApplication.translate("DialogImportData", "Import data"),
            QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.restore_configuration()

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogImportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_import_models(self):
        message_error = None

        if not self.xtf_file_line_edit.text().strip():
            color = '#ffd356'  # Light orange
            self.import_models_qmodel = QStandardItemModel()
            self.import_models_list_view.setModel(self.import_models_qmodel)
        else:

            if os.path.isfile(self.xtf_file_line_edit.text().strip()):
                color = '#fff'  # White

                self.import_models_qmodel = QStandardItemModel()
                models_name = self.find_models_xtf(
                    self.xtf_file_line_edit.text().strip())

                for model_name in models_name:
                    if not model_name in DEFAULT_HIDDEN_MODELS:
                        item = QStandardItem(model_name)
                        item.setCheckable(False)
                        item.setEditable(False)
                        self.import_models_qmodel.appendRow(item)

                if self.import_models_qmodel.rowCount() > 0:
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
                else:
                    message_error = QCoreApplication.translate(
                        "DialogImportData",
                        "No models were found in the XTF. Is it a valid file?")
                    color = '#ffd356'  # Light orange
                    self.import_models_qmodel = QStandardItemModel()
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
            else:
                message_error = QCoreApplication.translate(
                    "DialogImportData", "Please set a valid XTF file")
                color = '#ffd356'  # Light orange
                self.import_models_qmodel = QStandardItemModel()
                self.import_models_list_view.setModel(
                    self.import_models_qmodel)
        self.xtf_file_line_edit.setStyleSheet(
            'QLineEdit {{ background-color: {} }}'.format(color))

        if message_error:
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

    def find_models_xtf(self, xtf_path):
        models_name = list()
        pattern = re.compile(r'<MODEL[^>]*>(?P<text>[^<]*)</MODEL>')
        with open(xtf_path, 'r') as f:
            for txt in pattern.finditer(f.read()):
                model_tag = str(txt.group(0))
                name = re.findall('NAME="(.*?)"', model_tag, re.IGNORECASE)
                models_name.extend(name)
        return models_name

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.import_models_qmodel.rowCount()):
            item = self.import_models_qmodel.item(index)
            ili_models.append(item.text())
        return ili_models

    def show_settings(self):
        dlg = self.qgis_utils.get_settings_dialog()
        dlg.set_action_type(EnumDbActionType.IMPORT)
        dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX)
        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_connection_info()

    def accepted(self):
        configuration = self.update_configuration()

        if not os.path.isfile(self.xtf_file_line_edit.text().strip()):
            message_error = 'Please set a valid XTF file before importing data. XTF file does not exist'
            self.txtStdout.setText(
                QCoreApplication.translate("DialogImportData", message_error))
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            message_error = 'Please set a valid XTF before importing data.'
            self.txtStdout.setText(
                QCoreApplication.translate("DialogImportData", message_error))
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            message_error = QCoreApplication.translate(
                "DialogImportData",
                "The selected XTF file does not have information according to the LADM-COL model to import."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()
            self.progress_bar.setValue(0)

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            dataImporter = iliimporter.Importer(dataImport=True)

            item_db = self._conf_db.get_db_items()[self.db.mode]

            dataImporter.tool_name = item_db.get_model_baker_tool_name()
            dataImporter.configuration = configuration

            self.save_configuration(configuration)

            dataImporter.stdout.connect(self.print_info)
            dataImporter.stderr.connect(self.on_stderr)
            dataImporter.process_started.connect(self.on_process_started)
            dataImporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if dataImporter.run() != iliimporter.Importer.SUCCESS:
                    self.enable()
                    self.progress_bar.hide()
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogImportData",
                            "An error occurred when importing the data. For more information see the log..."
                        ), Qgis.Warning)
                    return
            except JavaNotFoundError:

                # Set JAVA PATH
                get_java_path_dlg = DialogGetJavaPath()
                get_java_path_dlg.setModal(True)
                if get_java_path_dlg.exec_():
                    configuration = self.update_configuration()

                if not get_java_path_from_qgis_model_baker():
                    message_error_java = QCoreApplication.translate(
                        "DialogImportData",
                        """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again."""
                    )
                    self.txtStdout.setTextColor(QColor('#000000'))
                    self.txtStdout.clear()
                    self.txtStdout.setText(message_error_java)
                    self.show_message(message_error_java, Qgis.Warning)
                self.enable()
                self.progress_bar.hide()
                return

            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogImportData",
                    "Import of the data was successfully completed"),
                Qgis.Success)

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import',
            configuration.xtffile)
        settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_import'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option  by default use online model repository
        self.use_local_models = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            type=bool)
        if self.use_local_models:
            self.custom_model_directories = settings.value(
                'Asistente-LADM_COL/models/custom_models') if settings.value(
                    'Asistente-LADM_COL/models/custom_models') else None

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        item_db = self._conf_db.get_db_items()[self.db.mode]

        configuration = ImportDataConfiguration()
        item_db.set_db_configuration_params(self.db.dict_conn_params,
                                            configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        configuration.delete_data = False

        configuration.epsg = DEFAULT_EPSG
        configuration.inheritance = DEFAULT_INHERITANCE
        configuration.create_basket_col = CREATE_BASKET_COL
        configuration.create_import_tid = CREATE_IMPORT_TID
        configuration.stroke_arcs = STROKE_ARCS

        java_path = get_java_path_from_qgis_model_baker()
        if java_path:
            self.base_configuration.java_path = java_path

        # Check custom model directories
        if self.use_local_models:
            if self.custom_model_directories is None:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append('Finished ({})'.format(exit_code))
        if result == iliimporter.Importer.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.show_message(
                QCoreApplication.translate("DialogImportData",
                                           "Error when importing data"),
                Qgis.Warning)
            self.enable()

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: create table structure...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(60)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(80)
            QCoreApplication.processEvents()

    def show_help(self):
        self.qgis_utils.show_help("import_data")

    def disable(self):
        self.db_config.setEnabled(False)
        self.ili_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.db_config.setEnabled(True)
        self.ili_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)
class TemporalSpectralProfilePlugin(object):

    POINT_SELECTION = 0
    SELECTED_POLYGON = 1

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.wdg = None
        self.pointTool = None

    def initGui(self):
        # create action
        self.action = QAction(
            QIcon(
                ":/plugins/temporalprofiletool/icons/temporalProfileIcon.png"),
            "Temporal/Spectral Profile", self.iface.mainWindow())
        self.action.setWhatsThis("Plots temporal/spectral profiles")
        self.action.triggered.connect(self.run)
        self.aboutAction = QAction("About", self.iface.mainWindow())
        self.aboutAction.triggered.connect(self.about)

        # add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.aboutAction)

        #Init class variables
        self.pointTool = ProfiletoolMapTool(self.iface.mapCanvas(),
                                            self.action)  #the mouselistener
        self.dockOpened = False  #remember for not reopening dock if there's already one opened
        self.mdl = None  #the model whitch in are saved layers analysed caracteristics
        self.selectionmethod = 0  #The selection method defined in option
        self.saveTool = self.canvas.mapTool(
        )  #Save the standard mapttool for restoring it at the end
        self.plotlibrary = None  #The plotting library to use
        self.pointSelectionInstructions = "Click on a raster for temporal/spectral profile (right click to cancel then quit)"
        self.selectedPolygonInstructions = 'Use "Select Features" tool to select polygon(s) designating AOI for which temporal/spectral profile should be calculated'

    def unload(self):
        if not self.wdg is None:
            self.wdg.close()
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu("&Profile Tool", self.action)
        self.iface.removePluginMenu("&Profile Tool", self.aboutAction)

    def run(self):
        # first, check posibility
        if self.checkIfOpening() == False:
            return

        #if dock not already opened, open the dock and all the necessary thing (model,doProfile...)
        if self.dockOpened == False:
            self.mdl = QStandardItemModel(0, 6)
            self.wdg = PTDockWidget(self.iface.mainWindow(), self.iface,
                                    self.mdl)
            self.wdg.showIt()
            self.doprofile = DoProfile(self.iface, self.wdg, self.pointTool,
                                       self)
            self.tableViewTool = TableViewTool()
            self.wdg.closed.connect(self.cleaning2)
            self.wdg.tableView.clicked.connect(self._onClick)
            self.wdg.pushButton_2.clicked.connect(self.addLayer)
            self.wdg.pushButton.clicked.connect(self.removeLayer)
            self.wdg.comboBox.currentIndexChanged.connect(self.selectionMethod)
            self.wdg.cboLibrary.currentIndexChanged.connect(
                self.changePlotLibrary)
            self.wdg.cboXAxis.currentIndexChanged.connect(
                self.changeXAxisLabeling)
            self.wdg.leXAxisSteps.editingFinished.connect(
                self.changeXAxisLabeling)
            self.wdg.dateTimeEditCurrentTime.editingFinished.connect(
                self.changeXAxisLabeling)
            self.wdg.spinBoxTimeExtent.editingFinished.connect(
                self.changeXAxisLabeling)
            self.wdg.cboTimeExtent.currentIndexChanged.connect(
                self.changeXAxisLabeling)
            self.wdg.cbTimeDimension.stateChanged.connect(
                self.changeXAxisLabeling)
            self.wdg.addOptionComboboxItems()
            self.addLayer()
            self.dockOpened = True
        #Listeners of mouse
        self.connectPointMapTool()
        #init the mouse listener comportement and save the classic to restore it on quit
        self.canvas.setMapTool(self.pointTool)

        #Help about what doing
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(
                self.pointSelectionInstructions)
        elif self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.iface.mainWindow().statusBar().showMessage(
                self.selectedPolygonInstructions)

        QgsProject.instance().layersWillBeRemoved.connect(
            self.onLayersWillBeRemoved)

#************************************* Canvas listener actions **********************************************

# Used when layer is about to be removed from QGIS Map Layer Registry

    def onLayersWillBeRemoved(self, layersIds):
        if self.mdl is not None:
            for layerId in layersIds:
                for row in range(self.mdl.rowCount()):
                    if layerId == self.mdl.index(row, 3).data().id():
                        self.removeLayer(row)
                        self.onLayersWillBeRemoved(layersIds)
                        break

    # Use for selected polygon option
    def selectionChanged(self, layer):
        if not layer.geometryType() == QgsWkbTypes.PolygonGeometry:
            return
        fullGeometry = QgsGeometry()
        for feature in layer.selectedFeatures():
            if fullGeometry.isEmpty():
                fullGeometry = QgsGeometry(feature.geometry())
            else:
                fullGeometry = fullGeometry.combine(feature.geometry())
        if not fullGeometry.isEmpty():
            crs = osr.SpatialReference()
            crs.ImportFromProj4(str(layer.crs().toProj4()))
            self.doprofile.calculatePolygonProfile(fullGeometry, crs, self.mdl,
                                                   self.plotlibrary)

#************************************* Mouse listener actions ***********************************************
# Used for point selection option

    def moved(self, point):
        if self.wdg and not self.wdg.cbPlotWhenClick.isChecked():
            if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
                self.doubleClicked(point)
            if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
                pass

    def rightClicked(self, point):  #used to quit the current action
        self.cleaning()

    def leftClicked(self, point):
        self.doubleClicked(point)

    def doubleClicked(self, point):
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(
                str(point.x()) + ", " + str(point.y()))
            self.doprofile.calculatePointProfile(point, self.mdl,
                                                 self.plotlibrary)
        if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            return

#***************************** open and quit options *******************************************

    def checkIfOpening(self):
        if self.iface.mapCanvas().layerCount() == 0:  #Check a layer is opened
            QMessageBox.warning(self.iface.mainWindow(), "Profile",
                                "First open a raster layer, please")
            return False

        layer = self.iface.activeLayer()

        if layer == None or not isProfilable(
                layer):  #Check if a raster layer is opened and selected
            if self.mdl == None or self.mdl.rowCount() == 0:
                QMessageBox.warning(self.iface.mainWindow(), "Profile Tool",
                                    "Please select a raster layer")
                return False

        return True

    def connectPointMapTool(self):
        self.pointTool.moved.connect(self.moved)
        self.pointTool.rightClicked.connect(self.rightClicked)
        self.pointTool.leftClicked.connect(self.leftClicked)
        self.pointTool.doubleClicked.connect(self.doubleClicked)

    def deactivatePointMapTool(self):  #enable clean exit of the plugin
        self.pointTool.moved.disconnect(self.moved)
        self.pointTool.leftClicked.disconnect(self.leftClicked)
        self.pointTool.rightClicked.disconnect(self.rightClicked)
        self.pointTool.doubleClicked.disconnect(self.doubleClicked)
        self.iface.mainWindow().statusBar().showMessage("")

    def connectSelectedPolygonsTool(self):
        self.iface.mapCanvas().selectionChanged.connect(self.selectionChanged)

    def deactivateSelectedPolygonsTools(self):
        self.iface.mapCanvas().selectionChanged.disconnect(
            self.selectionChanged)

    def cleaning(self):  #used on right click
        self.canvas.unsetMapTool(self.pointTool)
        self.canvas.setMapTool(self.saveTool)
        self.iface.mainWindow().statusBar().showMessage("")

    def cleaning2(self):  #used when Dock dialog is closed
        self.wdg.tableView.clicked.disconnect(self._onClick)
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.deactivatePointMapTool()
        else:
            self.deactivateSelectedPolygonsTools()
        self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
        self.wdg.comboBox.currentIndexChanged.disconnect(self.selectionMethod)
        QgsProject.instance().layersWillBeRemoved.disconnect(
            self.onLayersWillBeRemoved)
        self.mdl = None
        self.dockOpened = False
        self.cleaning()
        self.wdg = None

    #***************************** Options *******************************************

    def selectionMethod(self, item):
        if item == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
            self.pointTool.setCursor(Qt.CrossCursor)
            self.deactivateSelectedPolygonsTools()
            self.connectPointMapTool()
            if not self.canvas.mapTool() == self.pointTool:
                self.canvas.setMapTool(self.pointTool)
            self.iface.mainWindow().statusBar().showMessage(
                self.pointSelectionInstructions)
            self.wdg.changeStatComboBoxItems(
                self.doprofile.getPointProfileStatNames())

        elif item == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.selectionmethod = TemporalSpectralProfilePlugin.SELECTED_POLYGON
            self.deactivatePointMapTool()
            self.connectSelectedPolygonsTool()
            self.iface.actionSelectRectangle().trigger()
            self.iface.mainWindow().statusBar().showMessage(
                self.selectedPolygonInstructions)
            self.wdg.changeStatComboBoxItems(
                self.doprofile.getPolygonProfileStatNames(), "mean")

    def changePlotLibrary(self, item):
        self.plotlibrary = self.wdg.cboLibrary.itemText(item)
        self.wdg.addPlotWidget(self.plotlibrary)
        self.changeXAxisLabeling()

    def changeXAxisLabeling(self):
        self.xAxisSteps = {}
        # default band number labeling
        if self.wdg.cboXAxis.currentIndex() == 0:
            self.doprofile.xAxisSteps = None
        # Labels from string
        elif self.wdg.cboXAxis.currentIndex() == 1:
            self.doprofile.xAxisSteps = self.wdg.leXAxisSteps.text().split(';')
            try:
                self.doprofile.xAxisSteps = [
                    float(x) for x in self.doprofile.xAxisSteps
                ]
            except ValueError:
                self.doprofile.xAxisSteps = None
                text = "Temporal/Spectral Profile Tool: The X-axis steps' string " + \
                              "is invalid. Using band numbers instead."
                self.iface.messageBar().pushWidget(
                    self.iface.messageBar().createMessage(text),
                    QgsMessageBar.WARNING, 5)
        # Labels based on time
        elif self.wdg.cboXAxis.currentIndex() == 2:
            self.doprofile.xAxisSteps = [
                "Timesteps",
                self.wdg.dateTimeEditCurrentTime.dateTime().toPyDateTime(),
                int(self.wdg.spinBoxTimeExtent.cleanText()),
                self.wdg.cboTimeExtent.currentText(),
                self.wdg.cbTimeDimension.isChecked()
            ]
            if self.plotlibrary == "Qwt5":
                text = "Temporal/Spectral Profile Tool: There is currently no support using " + \
                              "Time steps while using the Qwt plotlibrary"
                self.iface.messageBar().pushWidget(
                    self.iface.messageBar().createMessage(text),
                    QgsMessageBar.WARNING, 5)
                self.doprofile.xAxisSteps = None

    #************************* tableview function ******************************************

    def addLayer(self):
        layer = self.iface.activeLayer()
        if isProfilable(layer):
            self.tableViewTool.addLayer(self.iface, self.mdl, layer)

    def _onClick(self, index1):  #action when clicking the tableview
        self.tableViewTool.onClick(self.iface, self.wdg, self.mdl,
                                   self.plotlibrary, index1)

    def removeLayer(self, index=None):
        if index is None or index is False:
            index = self.tableViewTool.chooseLayerForRemoval(
                self.iface, self.mdl)

        if index is not None:
            self.tableViewTool.removeLayer(self.mdl, index)
            PlottingTool().clearData(self.wdg, self.mdl, self.plotlibrary)

    def about(self):
        from .ui.dlgabout import DlgAbout
        DlgAbout(self.iface.mainWindow()).exec_()
Example #11
0
class Dialog(QDialog, Ui_nbEditor_dialog):
    def __init__(self, iface, ml, mc):
        """Constructor for the dialog.
        
        Args:
          iface: QgsInterface instance.
        """

        QDialog.__init__(self, iface.mainWindow())

        self.setupUi(self)

        self.ml = ml
        self.mCanvas = mc
        self.mRubberBand = QgsRubberBand(self.mCanvas, True)
        self.mRubberBand.reset(QgsWkbTypes.PolygonGeometry)
        self.mRubberBand.setColor(Qt.red)
        self.mRubberBand.setWidth(2)
        self.ids = []

        self.ini(0)

        self.pushCancel.clicked.connect(self.close)
        self.pushOK.clicked.connect(self.convert)
        self.comboBox.addItems(
            ['', 'Intersections', 'Touches', 'Within distance'])

        self.comboBox.currentIndexChanged.connect(self.nbMethod)
        self.ml.selectionChanged.connect(self.map2tab)

    def ini(self, n):
        self.model = QStandardItemModel(n, 1)
        self.tableView.setModel(self.model)
        self.model.setHeaderData(0, Qt.Horizontal, 'Neighbouring IDs')
        self.tableView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.selectionModel = QItemSelectionModel(self.model)
        self.tableView.setSelectionModel(self.selectionModel)
        self.tableView.horizontalHeader().setStretchLastSection(True)
        self.tableView.selectionModel().selectionChanged.connect(self.tab2map)
        self.progressBar.setValue(0)

    def settings(self):
        self.mod = min(self.ids)
        self.p = 1
        if self.mod == 1:
            self.p = 0

    def map2tab(self):
        s = ''
        idx = self.tableView.selectionModel().selectedIndexes()[0]
        ts = str(self.model.itemData(idx)[0])

        for fid in sorted(self.ml.selectedFeatureIds()):
            s += '%s,' % str(int(fid) + self.p)

        s = s[:-1]

        if s != ts:
            self.model.setData(idx, s)

        # in order to handle the symmetry
        if len(s) > len(ts):
            iLst = s.strip().replace(' ', '').split(',')
            jLst = ts.strip().replace(' ', '').split(',')
        else:
            iLst = ts.strip().replace(' ', '').split(',')
            jLst = s.strip().replace(' ', '').split(',')

        cent = str(idx.row() + self.p)
        dLst = list(set(iLst) - set(jLst))

        for d in dLst:
            row = int(d) - self.p
            sor = str(self.model.itemData(self.model.index(row, 0))[0])
            eLst = sor.strip().replace(' ', '').split(',')
            res = ''
            if cent in set(eLst):
                ii = eLst.index(cent)
                del eLst[ii]
                eLst = sorted(map(int, eLst))
                for e in eLst:
                    res += '%s,' % e
                res = res[:-1]
            else:
                u = sor + ',%s' % cent
                eLst = sorted(map(int, u.strip().replace(' ', '').split(',')))
                for e in eLst:
                    res += '%s,' % e
                res = res[:-1]

            self.model.setData(self.model.index(row, 0, QModelIndex()), res)

    def nbWithinDist(self):
        dlg = xdist.Dialog()
        dlg.setModal(True)
        dlg.setWindowTitle("Between two objects")

        if dlg.exec_() == QDialog.Accepted:
            lDist = float(dlg.lineEdit.text())
            if lDist == 0:
                return

            feat = QgsFeature()
            provider = self.ml.dataProvider()
            e = provider.featureCount()

            self.settings()

            for ne in range(self.mod, e + self.mod):
                feat = QgsFeature()
                geom = QgsGeometry()
                fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
                if fiter.nextFeature(feat):
                    geom = QgsGeometry(feat.geometry())

                neighbours = self.hdist(feat, lDist)
                row = feat.id() - self.mod
                self.model.setData(self.model.index(row, 0, QModelIndex()),
                                   neighbours)
                self.progressBar.setValue(100 * ne / e)

    def hdist(self, feata, lDist):
        geoma = QgsGeometry(feata.geometry())
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        #self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount()))
        ne = 0
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            geomb = QgsGeometry(feat.geometry())
            if feata.id() != feat.id():
                if geoma.distance(geomb) <= lDist:
                    neighbours = neighbours + '%s,' % (feat.id() + self.p)
        return neighbours[:-1]

    def tab2map(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.ml.selectionChanged.disconnect(self.map2tab)

        idx = self.tableView.selectionModel().selectedIndexes()[0]
        featureId = idx.row() + self.p

        s = self.model.itemData(idx)
        lst = s[0].strip().replace(' ', '').split(',')

        self.ml.removeSelection()

        for sid in lst:
            self.ml.select(int(sid) - self.p)

        provider = self.ml.dataProvider()

        feat = QgsFeature()
        layer = QgsVectorLayerCache(self.ml, provider.featureCount())
        layer.featureAtId(idx.row() + self.mod, feat)
        geom = QgsGeometry(feat.geometry())

        self.mRubberBand.setToGeometry(geom, self.ml)
        self.mRubberBand.show()

        self.ml.selectionChanged.connect(self.map2tab)

        QApplication.restoreOverrideCursor()

    def closeEvent(self, event):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.ml.selectionChanged.disconnect(self.map2tab)
        self.ml.removeSelection()
        self.mRubberBand.hide()
        self.close()

        QApplication.restoreOverrideCursor()

    def convert(self):
        dlg = editordlg()
        dlg.setModal(True)
        dlg.setWindowTitle("Neighbour list in BUGS format")
        num = ""
        adj = ""
        sumNumNeigh = 0
        for row in range(0, self.model.rowCount()):
            ts = self.model.itemData(self.model.index(row, 0))
            lst = ts[0].strip().replace(' ', '').split(',')
            num += '%s, ' % len(lst)
            sumNumNeigh += len(lst)
            lst.reverse()
            sor = ', '.join(lst) + ','
            adj = adj + str(sor) + '\n'

        num = num[:-2]
        adj = adj[:-2]

        nblist = 'list(\nnum = c(%s),\nadj = c(%s),\nsumNumNeigh=%s)' % (
            num, adj, sumNumNeigh)
        dlg.plainTextEdit.appendPlainText(nblist)

        dlg.exec_()

    def nbMethod(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.ml.selectionChanged.disconnect(self.map2tab)
        self.model.removeRows(0, self.model.rowCount(QModelIndex()),
                              QModelIndex())
        n = self.ml.dataProvider().featureCount()
        self.ini(n)

        self.ids = []

        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        #self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, n))
        ne = 0
        feat = QgsFeature()
        while feats.nextFeature(feat):
            ne += 1
            #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            self.ids.append(feat.id())

        if self.comboBox.currentText() == "Touches":
            if self.ml.geometryType() == 0:
                return
            else:
                self.nbTouches()
        if self.comboBox.currentText() == "Intersections":
            if self.ml.geometryType() == 0:
                return
            else:
                self.nbIntersects()
        if self.comboBox.currentText() == "Within distance":
            self.nbWithinDist()

        self.ml.selectionChanged.connect(self.map2tab)

        QApplication.restoreOverrideCursor()

    def nbTouches(self):
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        e = provider.featureCount()

        self.settings()

        for ne in range(self.mod, e + self.mod):
            feat = QgsFeature()
            geom = QgsGeometry()
            fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
            if fiter.nextFeature(feat):
                geom = QgsGeometry(feat.geometry())

            neighbours = self.htouch(feat)
            row = feat.id() - self.mod
            self.model.setData(self.model.index(row, 0, QModelIndex()),
                               neighbours)
            self.progressBar.setValue(100 * ne / e)

    def htouch(self, feata):
        geoma = QgsGeometry(feata.geometry())
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        #self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount()))
        ne = 0
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            geomb = QgsGeometry(feat.geometry())
            if feata.id() != feat.id():
                if geoma.touches(geomb) == True:
                    neighbours = neighbours + '%s,' % (feat.id() + self.p)
        return neighbours[:-1]

    def nbIntersects(self):
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        e = provider.featureCount()

        self.settings()

        for ne in range(self.mod, e + self.mod):
            feat = QgsFeature()
            geom = QgsGeometry()
            fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
            if fiter.nextFeature(feat):
                geom = QgsGeometry(feat.geometry())

            neighbours = self.hintersect(feat)
            row = feat.id() - self.mod
            self.model.setData(self.model.index(row, 0, QModelIndex()),
                               neighbours)
            self.progressBar.setValue(100 * ne / e)

    def hintersect(self, feata):
        geoma = QgsGeometry(feata.geometry())
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        #self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount()))
        ne = 0
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            geomb = QgsGeometry(feat.geometry())
            if feata.id() != feat.id():
                if geoma.intersects(geomb) == True:
                    neighbours = neighbours + '%s,' % (feat.id() + self.p)
        return neighbours[:-1]
class ThinGreyscaleDialog(QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        self.iface = iface
        self.plugin_dir = dirname(__file__)
        self.THINGREYSCALE = self.tr('ThinGreyscale')
        self.BROWSE = self.tr('Browse')
        self.CANCEL = self.tr('Cancel')
        self.CLOSE = self.tr('Close')
        self.HELP = self.tr('Help')
        self.OK = self.tr('OK')
        self.DEFAULTPROVIDER = 'GTiff'
        self.DEFAULTEXTENSION = '.tif'
        self.EXTRAEXTENSION = ' *.tiff'
        super(ThinGreyscaleDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        self.showInfo("Connecting UI components")
        okButton = self.button_box.button(QDialogButtonBox.Ok)
        okButton.setText(self.OK)
        cancelButton = self.button_box.button(QDialogButtonBox.Cancel)
        cancelButton.setText(self.CANCEL)
        cancelButton.setEnabled(False)
        closeButton = self.button_box.button(QDialogButtonBox.Close)
        closeButton.setText(self.CLOSE)
        browseButton = self.browseButton
        browseButton.setText(self.BROWSE)
        self.calcHistPushButton.setEnabled(False)
        self.listModel = QStandardItemModel(self.levelsListView)
        self.levelsListView.setModel(self.listModel)
        self.levelsListView.sizeHintForColumn(20)
        #self.levelValuesCheckBox.setEnabled(False)
        # Help button
        helpButton = self.helpButton
        helpButton.setText(self.HELP)

        # Connect signals
        self.showInfo("Connecting signals")
        okButton.clicked.connect(self.startWorker)
        cancelButton.clicked.connect(self.killWorker)
        closeButton.clicked.connect(self.reject)
        helpButton.clicked.connect(self.help)
        browseButton.clicked.connect(self.browse)

        inpIndexCh = self.inputRaster.currentIndexChanged['QString']
        inpIndexCh.connect(self.layerchanged)
        bandCh = self.bandComboBox.currentIndexChanged['QString']
        bandCh.connect(self.bandChanged)
        #self.iface.legendInterface().itemAdded.connect(
        #    self.layerlistchanged)
        #self.iface.legendInterface().itemRemoved.connect(
        #    self.layerlistchanged)
        #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject)
        self.button_box.rejected.disconnect(self.reject)
        calchistPr = self.calcHistPushButton.clicked
        calchistPr.connect(self.calculateHistogram)
        sugglevPr = self.suggestlevelsPushButton.clicked
        sugglevPr.connect(self.suggestLevels)
        addlevPr = self.addlevelPushButton.clicked
        addlevPr.connect(self.addLevel)
        dellevPr = self.deletelevelsPushButton.clicked
        dellevPr.connect(self.removeLevel)

        maxvalCh = self.maxValueSpinBox.valueChanged
        maxvalCh.connect(self.minmaxvalueChanged)
        maxvalFi = self.maxValueSpinBox.editingFinished
        maxvalFi.connect(self.minmaxvalueEdFinished)
        minvalCh = self.minValueSpinBox.valueChanged
        minvalCh.connect(self.minmaxvalueChanged)
        minvalFi = self.minValueSpinBox.editingFinished
        minvalFi.connect(self.minmaxvalueEdFinished)

        # Set instance variables
        #self.mem_layer = None
        self.worker = None
        self.inputlayerid = None
        self.inputlayer = None
        self.layerlistchanging = False
        self.minvalue = 1
        self.inputrasterprovider = None
        self.histobins = 50
        self.setupScene = QGraphicsScene(self)
        self.histoGraphicsView.setScene(self.setupScene)
        # Is the layer band of an integer type
        self.intband = False
        self.histogramAvailable = False
        self.histo = None
        self.histopadding = 1

    def startWorker(self):
        """Initialises and starts the worker thread."""
        try:
            layerindex = self.inputRaster.currentIndex()
            layerId = self.inputRaster.itemData(layerindex)
            inputlayer = QgsProject.instance().mapLayer(layerId)
            #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
            if inputlayer is None:
                self.showError(self.tr('No input layer defined'))
                return
            # create a reference to the layer that is being processed
            # (for use when creating the resulting raster layer)
            self.thinninglayer = inputlayer
            self.levels = []
            #self.levelsListView.selectAll()
            #selected = self.levelsListView.selectedIndexes()
            if self.levelsListView.model().rowCount() == 0:
                self.showInfo("Levels must be specified!")
                return
            for i in range(self.levelsListView.model().rowCount()):
                levelstring = self.levelsListView.model().item(i).text()
            #for i in selected:
            #    levelstring = self.levelsListView.model().itemData(i)[0]
                if self.intband:
                    self.levels.append(int(levelstring))
                else:
                    self.levels.append(float(levelstring))
            #self.levelsListView.clearSelection()
            # create a new worker instance
            worker = Worker(inputlayer, self.levels, self.intband)
            # configure the QgsMessageBar
            msgBar = self.iface.messageBar().createMessage(
                                        self.tr('Skeletonising'), '')
            self.aprogressBar = QProgressBar()
            self.aprogressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            acancelButton = QPushButton()
            acancelButton.setText(self.CANCEL)
            acancelButton.clicked.connect(self.killWorker)
            msgBar.layout().addWidget(self.aprogressBar)
            msgBar.layout().addWidget(acancelButton)
            # Has to be popped after the thread has finished (in
            # workerFinished).
            self.iface.messageBar().pushWidget(msgBar,
                                        Qgis.Info)
            self.messageBar = msgBar
            # start the worker in a new thread
            thread = QThread(self)
            worker.moveToThread(thread)
            worker.finished.connect(self.workerFinished)
            worker.error.connect(self.workerError)
            worker.status.connect(self.workerInfo)
            worker.progress.connect(self.progressBar.setValue)
            worker.progress.connect(self.aprogressBar.setValue)
            worker.iterprogress.connect(self.iterProgressBar.setValue)
            thread.started.connect(worker.run)
            thread.start()
            self.thread = thread
            self.worker = worker
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
            self.button_box.button(QDialogButtonBox.Close).setEnabled(False)
            self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True)
        except:
            import traceback
            self.showError(traceback.format_exc())
        else:
            pass

    def workerFinished(self, ok, ret):
        """Handles the output from the worker and cleans up after the
           worker has finished."""
        # clean up the worker and thread
        self.showInfo("Handling the result")
        self.worker.deleteLater()
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()
        # remove widget from message bar (pop)
        self.iface.messageBar().popWidget(self.messageBar)
        if ok and ret is not None:
            #self.showInfo("Ret: "+str(ret[10,]))
            # Transformation:
            self.minx = self.thinninglayer.extent().xMinimum()
            self.maxx = self.thinninglayer.extent().xMaximum()
            self.miny = self.thinninglayer.extent().yMinimum()
            self.maxy = self.thinninglayer.extent().yMaximum()
            self.rows = self.thinninglayer.height()
            self.cols = self.thinninglayer.width()
            self.xres = (self.maxx - self.minx) / float(self.cols)
            self.yres = (self.maxy - self.miny) / float(self.rows)
            geotransform = (self.minx, self.xres, 0, self.maxy, 0, -self.yres)
            try:
                format = self.DEFAULTPROVIDER
                driver = gdal.GetDriverByName(format)
                NOVALUE = 0
                metadata = driver.GetMetadata()
                fileName = self.outputRaster.text()
                if self.outputRaster.text() == "":
                    self.showInfo("No output file specified, " +
                                         "creating a temporary file")
                    # Get a temporary file
                    fileName = mktemp(prefix='greyskel',
                           suffix=self.DEFAULTEXTENSION)
                fileInfo = QFileInfo(fileName)
                filepath = fileInfo.absolutePath()
                baseName = fileInfo.baseName()
                suffix = fileInfo.suffix()
                thisfilename = filepath + baseName + '.' + suffix
                thisfilename = fileName
                self.showInfo("File name: " + thisfilename)
                gdaldatatype = gdal.GDT_Byte
                skelmatrix = None
                if self.levelValuesCheckBox.isChecked():
                    # Transform the pixel values back to the original
                    # level values
                    my_dict = {}
                    # Add zero to handle the "empty" pixels
                    my_dict[0] = 0
                    for i in range(len(self.levels)):
                        my_dict[i + 1] = self.levels[i]
                    skelmatrix = np.vectorize(my_dict.__getitem__,
                                              otypes=[np.float])(ret)
                    gdaldatatype = gdal.GDT_Int32
                    if not self.intband:
                        gdaldatatype = gdal.GDT_Float32
                else:
                    skelmatrix = ret
                outDataset = driver.Create(thisfilename, self.cols,
                                           self.rows, 1, gdaldatatype)
                if self.thinninglayer.dataProvider().crs() is not None:
                    srs = self.thinninglayer.dataProvider().crs()
                    outDataset.SetProjection(srs.toWkt().encode('ascii',
                                                               'ignore'))
                skeletonband = outDataset.GetRasterBand(1)
                skeletonband.WriteArray(skelmatrix)
                skeletonband.SetNoDataValue(NOVALUE)
                #stats = skeletonband.GetStatistics(False, True)
                #skeletonband.SetStatistics(stats[0], stats[1],
                #                                 stats[2], stats[3])
                outDataset.SetGeoTransform(geotransform)
                outDataset = None  # To close the file
                # report the result
                rlayer = QgsRasterLayer(thisfilename, baseName)
                self.layerlistchanging = True
                #QgsMapLayerRegistry.instance().addMapLayer(rlayer)
                QgsProject.instance().addMapLayer(rlayer)
                self.layerlistchanging = False
            except:
                import traceback
                self.showError("Can't write the skeleton file:  %s" %
                                   self.outputRaster.text() + ' - ' +
                                   traceback.format_exc())
                okb = self.button_box.button(QDialogButtonBox.Ok)
                okb.setEnabled(True)
                closb = self.button_box.button(QDialogButtonBox.Close)
                closb.setEnabled(True)
                cancb = self.button_box.button(QDialogButtonBox.Cancel)
                cancb.setEnabled(False)
                return
            QgsMessageLog.logMessage(self.tr('ThinGreyscale finished'),
                                self.THINGREYSCALE, Qgis.Info)
        else:
            # notify the user that something went wrong
            if not ok:
                self.showError(self.tr('Aborted') + '!')
            else:
                self.showError(self.tr('No skeleton created') + '!')
        self.progressBar.setValue(0.0)
        #self.aprogressBar.setValue(0.0)
        self.iterProgressBar.setValue(0.0)
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
        self.button_box.button(QDialogButtonBox.Close).setEnabled(True)
        self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False)

    def workerError(self, exception_string):
        """Report an error from the worker."""
        #QgsMessageLog.logMessage(self.tr('Worker failed - exception') +
        #                         ': ' + str(exception_string),
        #                         self.THINGREYSCALE,
        #                         QgsMessageLog.CRITICAL)
        self.showError(exception_string)

    def workerInfo(self, message_string):
        """Report an info message from the worker."""
        QgsMessageLog.logMessage(self.tr('Worker') + ': ' + message_string,
                                 self.THINGREYSCALE, Qgis.Info)

    def layerchanged(self, number=0):
        """Do the necessary updates after a layer selection has
           been changed."""
        self.showInfo("Layer changed")
        # If the layer list is being updated, don't do anything
        if self.layerlistchanging:
            return
        layerindex = self.inputRaster.currentIndex()
        layerId = self.inputRaster.itemData(layerindex)
        self.inputlayerid = layerId
        #self.inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
        self.inputlayer = QgsProject.instance().mapLayer(layerId)
        if self.inputlayer is not None:
            self.inputrasterprovider = self.inputlayer.dataProvider()
            self.bandComboBox.clear()
            bandcount = self.inputlayer.bandCount()
            #self.showInfo("Layer bandcount: "+str(bandcount))
            for i in range(bandcount):
                self.bandComboBox.addItem(self.inputlayer.bandName(i + 1), i)
                #self.showInfo("Band " + str(i) + ": " +
                #                self.inputlayer.bandName(i+1))
            # Check if the driver supports Create() or CreateCopy()
            #gdalmetadata = self.inputlayer.metadata()
            #self.showInfo("Layer metadata: " +
            #                str(gdalmetadata.encode('utf-8')))
            #provstring = '<p>GDAL provider</p>\n'
            #providerpos = gdalmetadata.find(provstring)
            #brpos = gdalmetadata.find('<br>', providerpos + len(provstring))
            #self.gdalprovider = gdalmetadata[int(providerpos +
            #                             len(provstring)):int(brpos)]
            #self.showInfo('GDAL provider: '+self.gdalprovider)
            #drivername = self.gdalprovider.encode('ascii', 'ignore')
            #theDriver = gdal.GetDriverByName(drivername)
            #if theDriver is None:
            #    self.showInfo("Unable to get the raster driver")
            #else:
            #data    theMetadata = theDriver.GetMetadata()
                #self.showInfo("Driver metadata: "+str(theMetadata))
                #if ((gdal.DCAP_CREATE in theMetadata) and
                #        theMetadata[gdal.DCAP_CREATE] == 'YES'):
                #    self.canCreate = True
                #if (theMetadata.has_key(gdal.DCAP_CREATECOPY) and
                #if ((gdal.DCAP_CREATECOPY in theMetadata) and
                #        theMetadata[gdal.DCAP_CREATECOPY] == 'YES'):
                #    self.canCreateCopy = True
                #self.showInfo('raster provider type: ' +
                #                str(self.inputlayer.providerType()))
                # Determine the file suffix
                #self.gdalext = ""
                #if gdal.DMD_EXTENSION in theMetadata:
                #    self.gdalext = "." + theMetadata[gdal.DMD_EXTENSION]
                #else:
                #    self.showInfo("No extension available in GDAL metadata")
                # by parsing the layer metadata looking for
                #           "Dataset Description"
                #descstring = 'Dataset Description</p>\n<p>'
                #descpos = gdalmetadata.find(descstring)
                #ppos = gdalmetadata.find('</p>',descpos+len(descstring))
                #filename = gdalmetadata[descpos+len(descstring):ppos]
                #self.gdalext = splitext(filename)[1]
                #self.showInfo('GDAL extension: '+self.gdalext)
                # Determine the datatype
                #datatypestring = 'Data Type</p>\n<p>'
                #datatypepos = gdalmetadata.find(datatypestring)
                #ppos = gdalmetadata.find('</p>',
                #                   datatypepos + len(datatypestring))
                #datatypedesc = gdalmetadata[datatypepos +
                #                            len(datatypestring):ppos]
                #shortdesc = datatypedesc.split()[0]
                #self.showInfo('GDAL data type: GDT_'+shortdesc)
                # Call the findGdalDatatype function
                #self.findGdalDatatype(shortdesc)
            #   self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            self.calcHistPushButton.setEnabled(True)
            self.suggestlevelsPushButton.setEnabled(True)

    def bandChanged(self):
        band = self.bandComboBox.currentIndex() + 1
        self.showInfo("Band changed: " + str(band))
        statistics = self.inputrasterprovider.bandStatistics(band)
        #self.showInfo("Band statistics: " + str(statistics.minimumValue) +
        #                            " - " + str(statistics.maximumValue) +
        #                            " - " + str(statistics.mean))
        self.bandmin = statistics.minimumValue
        self.bandmax = statistics.maximumValue
        dt = self.inputrasterprovider.dataType(band)
        # Integer data type
        if (dt == Qgis.Byte or dt == Qgis.UInt16 or dt == Qgis.Int16
                            or dt == Qgis.UInt32 or dt == Qgis.Int32):
            self.intband = True
            self.minValueSpinBox.setDecimals(0)
            self.maxValueSpinBox.setDecimals(0)
            self.levelSpinBox.setDecimals(0)
            self.bandMinLabel.setText(str(int(statistics.minimumValue)))
            self.bandMaxLabel.setText(str(int(statistics.maximumValue)))
        else:
            self.intband = False
            self.minValueSpinBox.setDecimals(5)
            self.maxValueSpinBox.setDecimals(5)
            self.levelSpinBox.setDecimals(5)
            minlabtext = "{0:.5f}".format(statistics.minimumValue)
            self.bandMinLabel.setText(minlabtext)
            maxlabtext = "{0:.5f}".format(statistics.maximumValue)
            self.bandMaxLabel.setText(maxlabtext)
        #self.minValueSpinBox.setMinimum(statistics.minimumValue)
        self.maxValueSpinBox.setMinimum(statistics.minimumValue)
        #self.minValueSpinBox.setMaximum(statistics.maximumValue)
        self.maxValueSpinBox.setMaximum(statistics.maximumValue)
        #self.minValueSpinBox.setValue(statistics.minimumValue)
        if not (statistics.statsGathered & statistics.Mean):
            bandmean = (statistics.minimumValue + statistics.maximumValue) / 2
        else:
            #self.showInfo("statsgathered: " + str(statistics.statsGathered))
            bandmean = statistics.mean
        if self.intband:
            self.minValueSpinBox.setValue(int(ceil(bandmean)))
        else:
            self.minValueSpinBox.setValue(bandmean)
        self.maxValueSpinBox.setValue(statistics.maximumValue)
        self.histMinValue.setText(str(statistics.minimumValue))
        self.histMaxValue.setText(str(statistics.maximumValue))
        self.levelSpinBox.setMinimum(statistics.minimumValue)
        self.levelSpinBox.setMaximum(statistics.maximumValue)
        self.histogramAvailable = False
        #if self.inputrasterprovider.hasStatistics(band):
        #if statistics.statsGathered:
            #histogram = statistics.histogramVector
            #self.showInfo("Histogram: " + str(histogram))
            #range = min to max
            #np.histogram(band, 50, range)

    def minmaxvalueChanged(self):
        #if self.minValueSpinBox is None:
        #    return
        minvalue = self.minValueSpinBox.value()
        #if minvalue is None:
        #    return
        #if self.maxValueSpinBox is None:
        #    return
        maxvalue = self.maxValueSpinBox.value()
        #if maxvalue is None:
        #   return
        if isnan(maxvalue) or isnan(minvalue):
            return
        self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " +
                                                       str(maxvalue))
        #if self.intband:
        #    minvalue = int(minvalue)
        #    maxvalue = int(maxvalue)
        if abs(maxvalue - minvalue) < 0.00001:
        #if maxvalue == maxvalue:
            self.calcHistPushButton.setEnabled(False)
        else:
            self.calcHistPushButton.setEnabled(True)
        # Update the min and max value spinboxes
        self.minValueSpinBox.setMaximum(maxvalue)
        self.maxValueSpinBox.setMinimum(minvalue)
        self.minValueSpinBox.setMinimum(self.bandmin)

    def minmaxvalueEdFinished(self):
        minvalue = self.minValueSpinBox.value()
        maxvalue = self.maxValueSpinBox.value()
        if self.intband:
            minvalue = int(minvalue)
            maxvalue = int(maxvalue)
        self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " +
                                    str(maxvalue))
        # Update the spin box for adding levels
        self.levelSpinBox.setMinimum(minvalue)
        self.levelSpinBox.setMaximum(maxvalue)
        if self.levelSpinBox.value() < minvalue:
            self.levelSpinBox.setValue(minvalue)
        if self.levelSpinBox.value() > maxvalue:
            self.levelSpinBox.setValue(maxvalue)
        # Update the min and max value spinboxes
        self.minValueSpinBox.setMaximum(maxvalue)
        self.maxValueSpinBox.setMinimum(minvalue)

        # Adjust the levels:
        i = 0
        while self.levelsListView.model().item(i):
        #for i in range(self.levelsListView.model().rowCount()):
            #self.showInfo("Element: " +
            #       str(self.levelsListView.model().item(i).text()))
            #continue
            value = float(self.levelsListView.model().item(i).text())
            if value < minvalue:
                if i == 0:
                    self.levelsListView.model().item(i).setText(str(minvalue))
                    i = i + 1
                else:
                    self.levelsListView.model().removeRow(i)
            elif value > maxvalue:
                if i == self.levelsListView.model().rowCount() - 1:
                    self.levelsListView.model().item(i).setText(str(maxvalue))
                    i = i + 1
                else:
                    self.levelsListView.model().removeRow(i)
            else:
                i = i + 1
        self.drawHistogram()

    def calculateHistogram(self):
        self.showInfo("Calculating histogram...")
        if self.inputlayer is None:
            return
        
        self.showInfo("Calculating histogram...")
        # Check if there is only one value
        myrange = (self.minValueSpinBox.value(),
                   self.maxValueSpinBox.value())
        self.inputextent = self.inputlayer.extent()
        self.inputrdp = self.inputlayer.dataProvider()
        width = self.inputlayer.width()
        height = self.inputlayer.height()
        if width == 0 or height == 0:
            self.showInfo("Image has zero width or height")
            return
        extwidth = self.inputextent.width()
        extheight = self.inputextent.height()
        # Read the raster block and get the maximum value
        rasterblock = self.inputrdp.block(1, self.inputextent,
                                          width, height)
        # Create a numpy array version of the image
        imageMat = np.zeros((height, width), dtype=np.float16)
        # This one takes a lot of time!
        for row in range(height):
            for column in range(width):
                imageMat[row, column] = rasterblock.value(row, column)
                self.showInfo("Image: " + str(height) + ", " + str(width) + " - " + str(imageMat[row, column]))
        self.histo = np.histogram(imageMat, self.histobins, myrange)
        #relevantpixels = imageMat[np.where(imageMat >= bandval)]
        minlevel = float(self.bandMinLabel.text())
        relevantpixels = imageMat[np.where(imageMat >= minlevel)]
        #self.showInfo("Histogram: " + str(self.histo))
        nanpercentage = 100.0 - 100.0 * len(relevantpixels) / (width * height)
        self.bandNANLabel.setText("{0:.1f}".format(nanpercentage))
        #self.showInfo("Percentage NAN: " + str(100.0 - 100.0 *
        #                    len(relevantpixels) / (width * height)))
        #self.showInfo("First element: " + str(self.histo[0]))
        #self.showInfo("First element, first: " + str(self.histo[0][0]))
        #self.showInfo("First element, second: " + str(self.histo[0][1]))
        self.histMinValue.setText(str(self.minValueSpinBox.value()))
        self.histMaxValue.setText(str(self.maxValueSpinBox.value()))
        if self.intband:
            self.histMinValue.setText(str(int(self.minValueSpinBox.value())))
            self.histMaxValue.setText(str(int(self.maxValueSpinBox.value())))
        self.histogramAvailable = True
        self.drawHistogram()

    def drawHistogram(self):
        #if self.inputlayer is None:
        #    return
        self.showInfo("Drawing histogram...")
        viewprect = QRectF(self.histoGraphicsView.viewport().rect())
        self.histoGraphicsView.setSceneRect(viewprect)
        self.setupScene.clear()
        self.setupScene.update()
        histbottom = self.histoGraphicsView.sceneRect().bottom()
        histtop = self.histoGraphicsView.sceneRect().top()
        left = self.histoGraphicsView.sceneRect().left() + self.histopadding
        right = self.histoGraphicsView.sceneRect().right() - self.histopadding
        histheight = histbottom - histtop
        histwidth = right - left
        step = 1.0 * histwidth / self.histobins
        maxlength = histheight
        padding = 1
        ll = QPoint(self.histopadding - 1, histheight - padding)
        start = QPointF(self.histoGraphicsView.mapToScene(ll))

        # Check if there is only one value
        #myrange = (self.minValueSpinBox.value(),self.maxValueSpinBox.value())
        if self.histogramAvailable:
            maxvalue = 0.0
            for i in range(len(self.histo[0])):
                if self.histo[0][i] > maxvalue:
                    maxvalue = self.histo[0][i]
            if maxvalue == 0:
                return
            self.maxBinNumber.setText(str(maxvalue))
            # Create the histogram:
            #self.showInfo("maxvalue: " + str(maxvalue))
            #self.showInfo("maxlength: " + str(maxlength))
            #self.showInfo("step: " + str(step))
            for i in range(self.histobins):
                binnumber = self.histo[0][i]
                if binnumber == 0:
                    continue
                height = (1.0 * self.histo[0][i] / maxvalue *
                                          (maxlength - padding))
                rectangle = QGraphicsRectItem(start.x() + step * i,
                                          start.y(),
                                          step,
                                          -height)
                rectangle.setPen(QPen(QColor(102, 102, 102)))
                rectangle.setBrush(QBrush(QColor(240, 240, 240)))
                self.setupScene.addItem(rectangle)
                #self.showInfo(str(i) + ": " + str(height))
            #if self.levelsListView.model().rowCount() > 0:
        # Add lines for the levels
        minvalue = float(self.histMinValue.text())
        maxvalue = float(self.histMaxValue.text())
        datarange = maxvalue - minvalue
        if datarange == 0:
            return
        i = 0
        while self.levelsListView.model().item(i):
            #self.showInfo("Element: " +
            #       str(self.levelsListView.model().item(i).text()))
            #continue
            value = float(self.levelsListView.model().item(i).text())
            xvalue = start.x() + histwidth * (value - minvalue) / datarange
            line = QGraphicsLineItem(xvalue, 0, xvalue, histheight)
            if i == 0 or i == (self.levelsListView.model().rowCount() - 1):
                line.setPen(QPen(QColor(204, 0, 0)))
            else:
                line.setPen(QPen(QColor(0, 204, 0)))
            self.setupScene.addItem(line)
            i = i + 1

    def suggestLevels(self):
        self.listModel.clear()
        self.showInfo("Suggesting levels")
        levels = self.levelsSpinBox.value()
        startvalue = self.minValueSpinBox.value()
        endvalue = self.maxValueSpinBox.value()
        increment = (endvalue - startvalue) / levels
        for i in range(levels + 1):
            value = startvalue + increment * i
            if self.intband:
                value = int(value)
            item = QStandardItem(str(value))
            self.listModel.appendRow(item)
        self.drawHistogram()

    def addLevel(self):
        newvalue = self.levelSpinBox.value()
        if self.intband:
            newvalue = int(newvalue)
        for i in range(self.listModel.rowCount()):
            # Check if the value is already in the list
            if self.listModel.item(i).text() == str(newvalue):
                return
            else:
                # Maintain a sorted list of distances
                if (float(self.listModel.item(i).text()) >
                                 float(str(newvalue))):
                    item = QStandardItem(str(newvalue))
                    self.listModel.insertRow(i, item)
                    self.drawHistogram()
                    return
        item = QStandardItem(str(newvalue))
        self.listModel.appendRow(item)
        #if self.histogramAvailable:
        #    addLevelsToHistogram()
        self.drawHistogram()

    def removeLevel(self):
        self.levelsListView.setUpdatesEnabled(False)
        indexes = self.levelsListView.selectedIndexes()
        indexes.sort()
        for i in range(len(indexes) - 1, -1, -1):
            self.listModel.removeRow(indexes[i].row())
        self.levelsListView.setUpdatesEnabled(True)
        #if self.histogramAvailable:
        #    removeLevelFromHistogram()
        self.drawHistogram()

    def layerlistchanged(self):
        self.layerlistchanging = True
        self.showInfo("Layer list changed")
        # Repopulate the input layer combo box
        # Save the currently selected input layer
        inputlayerid = self.inputlayerid
        self.inputRaster.clear()
        for alayer in self.iface.legendInterface().layers():
            if alayer.type() == QgsMapLayer.RasterLayer:
                gdalmetadata = alayer.metadata()
                # Skip WMS layers
                WMSstring = 'Web Map Service'
                wmspos = gdalmetadata.find(WMSstring)
                if wmspos != -1:
                    continue
                self.inputRaster.addItem(alayer.name(), alayer.id())
        # Set the previous selection
        for i in range(self.inputRaster.count()):
            if self.inputRaster.itemData(i) == inputlayerid:
                self.inputRaster.setCurrentIndex(i)
        self.layerlistchanging = False
        #self.updateui()

    def updateui(self):
        """Do the necessary updates after a layer selection has
           been changed."""
        #if self.layerlistchanged:
        #    return
        #self.outputRaster.setText(self.inputRaster.currentText() +
        #                           '_' + 'thinned')
        layerindex = self.inputRaster.currentIndex()
        layerId = self.inputRaster.itemData(layerindex)
        #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId)
        inputlayer = QgsProject.instance().mapLayer(layerId)
        if inputlayer is not None:
            pass
        else:
            pass

    def findGdalDatatype(self, shortdesc):
            gdaldatatype = None
            # // Unknown or unspecified type
            # GDT_Unknown = GDALDataType(C.GDT_Unknown)
            if shortdesc == 'Unknown':
                gdaldatatype = gdal.GDT_Unknown
            # // Eight bit unsigned integer
            # GDT_Byte = GDALDataType(C.GDT_Byte)
            elif shortdesc == 'Byte':
                gdaldatatype = gdal.GDT_Byte
            # // Sixteen bit unsigned integer
            # GDT_UInt16 = GDALDataType(C.GDT_UInt16)
            elif shortdesc == 'UInt16':
                gdaldatatype = gdal.GDT_UInt16
            # // Sixteen bit signed integer
            # GDT_Int16 = GDALDataType(C.GDT_Int16)
            elif shortdesc == 'Int16':
                gdaldatatype = gdal.GDT_Int16
            # // Thirty two bit unsigned integer
            # GDT_UInt32 = GDALDataType(C.GDT_UInt32)
            elif shortdesc == 'UInt32':
                gdaldatatype = gdal.GDT_UInt32
            # // Thirty two bit signed integer
            # GDT_Int32 = GDALDataType(C.GDT_Int32)
            elif shortdesc == 'Int32':
                gdaldatatype = gdal.GDT_Int32
            # // Thirty two bit floating point
            # GDT_Float32 = GDALDataType(C.GDT_Float32)
            elif shortdesc == 'Float32':
                gdaldatatype = gdal.GDT_Float32
            # // Sixty four bit floating point
            # GDT_Float64 = GDALDataType(C.GDT_Float64)
            elif shortdesc == 'Float64':
                gdaldatatype = gdal.GDT_Float64
            # // Complex Int16
            # GDT_CInt16 = GDALDataType(C.GDT_CInt16)
            elif shortdesc == 'CInt16':
                gdaldatatype = gdal.CInt16
            # // Complex Int32
            # GDT_CInt32 = GDALDataType(C.GDT_CInt32)
            elif shortdesc == 'CInt32':
                gdaldatatype = gdal.CInt32
            # // Complex Float32
            # GDT_CFloat32 = GDALDataType(C.GDT_CFloat32)
            elif shortdesc == 'CFloat32':
                gdaldatatype = gdal.CFloat32
            # // Complex Float64
            # GDT_CFloat64 = GDALDataType(C.GDT_CFloat64)
            elif shortdesc == 'CFloat64':
                gdaldatatype = gdal.CFloat64
            # // maximum type # + 1
            # GDT_TypeCount = GDALDataType(C.GDT_TypeCount)
            elif shortdesc == 'TypeCount':
                gdaldatatype = gdal.TypeCount
            self.gdaldatatype = gdaldatatype

    def killWorker(self):
        """Kill the worker thread."""
        if self.worker is not None:
            QgsMessageLog.logMessage(self.tr('Killing worker'),
                                     self.THINGREYSCALE, Qgis.Info)
            self.worker.kill()

    def showError(self, text):
        """Show an error."""
        self.iface.messageBar().pushMessage(self.tr('Error'), text,
                                            level=QgsMessageBar.CRITICAL,
                                            duration=3)
        QgsMessageLog.logMessage('Error: ' + text, self.THINGREYSCALE,
                                 QgsMessageLog.CRITICAL)

    def showWarning(self, text):
        """Show a warning."""
        self.iface.messageBar().pushMessage(self.tr('Warning'), text,
                                            level=QgsMessageBar.WARNING,
                                            duration=2)
        QgsMessageLog.logMessage('Warning: ' + text, self.THINGREYSCALE,
                                 QgsMessageLog.WARNING)

    def showInfo(self, text):
        """Show info."""
        self.iface.messageBar().pushMessage(self.tr('Info'), text,
                                            level=Qgis.Info,
                                            duration=2)
        QgsMessageLog.logMessage('Info: ' + text, self.THINGREYSCALE,
                                 Qgis.Info)

    # def help(self):
        # #QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir +
        #                                 "/help/build/html/index.html"))
        # QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir +
        #                                            "/help/index.html"))
        # #showPluginHelp()

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        return QCoreApplication.translate('ThinGreyScaleDialog', message)

    def browse(self):
        settings = QSettings()
        key = '/UI/lastShapefileDir'
        outDir = settings.value(key)
        home = outDir
        #outDir = expanduser("~")
        #filter = (self.DEFAULTPROVIDER + " (*" +
        #             self.DEFAULTEXTENSION + ");;All files (*)")
        filter = (self.DEFAULTPROVIDER + " (*" +
                     self.DEFAULTEXTENSION + self.EXTRAEXTENSION + ")")
        #if (self.gdalprovider != self.DEFAULTPROVIDER and
        #                                     (self.canCreateCopy or
        #                                           self.canCreate)):
        #    filter = (self.gdalprovider + " (*" + self.gdalext +
        #                                          ");;" + filter)
        outFilePath = QFileDialog.getSaveFileName(self,
                                   'Specify file name for skeleton',
                                                     outDir, filter)
        outFilePath = unicode(outFilePath)
        if outFilePath:
            root, ext = splitext(outFilePath)
            if ext.lower() != '.tif' and ext.lower() != '.tiff':
                outFilePath = '%s.tif' % outFilePath
            outDir = dirname(outFilePath)
            settings.setValue(key, outDir)
        #        (self.canCreateCopy or self.canCreate):
        #    fileName = splitext(str(fileName))[0]+self.gdalext
        self.outputRaster.setText(outFilePath)

    # Overriding
    def resizeEvent(self, event):
        #self.showInfo("resizeEvent")
        self.calculateHistogram()

    def help(self):
        #QDesktopServices.openUrl(QUrl.fromLocalFile(
        #                 self.plugin_dir + "/help/html/index.html"))
        showPluginHelp(None, "help/html/index")

    # Implement the accept method to avoid exiting the dialog when
    # starting the work
    def accept(self):
        """Accept override."""
        pass

    # Implement the reject method to have the possibility to avoid
    # exiting the dialog when cancelling
    def reject(self):
        """Reject override."""
        # exit the dialog
        QDialog.reject(self)
Example #13
0
class UploadOverview(uicls_log, basecls_log):
    """Upload status overview dialog."""

    MAX_SCHEMATISATION_MODELS = 3

    def __init__(self, plugin_dock, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.plugin_dock = plugin_dock
        self.threedi_api = self.plugin_dock.threedi_api
        self.tc = ThreediCalls(self.plugin_dock.threedi_api)
        self.communication = self.plugin_dock.communication
        self.feedback_logger = ListViewLogger(self.lv_upload_feedback)
        self.upload_thread_pool = QThreadPool()
        self.ended_tasks = OrderedDict()
        self.upload_progresses = defaultdict(lambda: ("NO TASK", 0.0, 0.0))
        self.current_upload_row = 0
        self.schematisation = None
        self.schematisation_sqlite = None
        self.schematisation_id = None
        self.pb_new_upload.clicked.connect(self.upload_new_model)
        self.pb_hide.clicked.connect(self.close)
        self.tv_model = None
        self.setup_view_model()
        self.adjustSize()

    @property
    def current_local_schematisation(self):
        """Return currently loaded local schematisation."""
        return self.plugin_dock.current_local_schematisation

    def setup_view_model(self):
        """Setting up model and columns for TreeView."""
        nr_of_columns = 4
        self.tv_model = QStandardItemModel(0, nr_of_columns - 1)
        self.tv_model.setHorizontalHeaderLabels(["Schematisation name", "Revision", "Commit message", "Status"])
        self.tv_uploads.setModel(self.tv_model)
        self.tv_uploads.selectionModel().selectionChanged.connect(self.on_upload_context_change)
        for i in range(nr_of_columns):
            self.tv_uploads.resizeColumnToContents(i)
        self.progress_widget.hide()
        self.label_success.hide()
        self.label_failure.hide()

    def on_upload_context_change(self):
        """Updating progress bars based on upload selection change."""
        selected_indexes = self.tv_uploads.selectedIndexes()
        if selected_indexes:
            current_index = selected_indexes[0]
            current_row = current_index.row()
            self.current_upload_row = current_row + 1
            self.on_update_upload_progress(self.current_upload_row, *self.upload_progresses[self.current_upload_row])
            self.feedback_logger.clear()
            try:
                for msg, success in self.ended_tasks[self.current_upload_row]:
                    if success:
                        self.feedback_logger.log_info(msg)
                    else:
                        self.feedback_logger.log_error(msg)
            except KeyError:
                pass
            status_item = self.tv_model.item(current_row, 3)
            status = status_item.text()
            if status == UploadStatus.SUCCESS.value:
                self.progress_widget.hide()
                self.label_success.show()
                self.label_failure.hide()
            elif status == UploadStatus.FAILURE.value:
                self.progress_widget.hide()
                self.label_success.hide()
                self.label_failure.show()
            else:
                self.progress_widget.show()
                self.label_success.hide()
                self.label_failure.hide()

    def add_upload_to_model(self, upload_specification):
        """Initializing a new upload."""
        create_revision = upload_specification["create_revision"]
        schematisation = upload_specification["schematisation"]
        schema_name_item = QStandardItem(f"{schematisation.name}")
        revision = upload_specification["latest_revision"]
        revision_number = revision.number + 1 if create_revision is True else revision.number
        revision_item = QStandardItem(f"{revision_number}")
        commit_msg_item = QStandardItem(f"{upload_specification['commit_message']}")
        status_item = QStandardItem(UploadStatus.IN_PROGRESS.value)
        self.tv_model.appendRow([schema_name_item, revision_item, commit_msg_item, status_item])
        upload_row_number = self.tv_model.rowCount()
        upload_row_idx = self.tv_model.index(upload_row_number - 1, 0)
        self.tv_uploads.selectionModel().setCurrentIndex(upload_row_idx, QItemSelectionModel.ClearAndSelect)
        worker = UploadProgressWorker(
            self.threedi_api, self.current_local_schematisation, upload_specification, upload_row_number
        )
        worker.signals.upload_progress.connect(self.on_update_upload_progress)
        worker.signals.thread_finished.connect(self.on_upload_finished_success)
        worker.signals.upload_failed.connect(self.on_upload_failed)
        worker.signals.revision_committed.connect(self.on_revision_committed)
        self.upload_thread_pool.start(worker)

    def upload_new_model(self):
        """Initializing new upload wizard."""
        if not self.current_local_schematisation or not self.current_local_schematisation.sqlite:
            warn_msg = "Please load the schematisation first before starting the upload."
            self.communication.show_warn(warn_msg, parent=self)
            self.plugin_dock.build_options.load_local_schematisation()
            return
        self.schematisation_sqlite = self.current_local_schematisation.sqlite
        schema_sqlite_loaded = is_toolbox_spatialite_loaded(self.schematisation_sqlite)
        if schema_sqlite_loaded is False:
            title = "Warning"
            question = (
                "Warning: the Spatialite that you loaded with the 3Di Toolbox is not in the revision you are "
                "about to upload. Do you want to continue?"
            )
            on_continue_answer = self.communication.ask(self, title, question, QMessageBox.Warning)
            if on_continue_answer is not True:
                return
        self.schematisation_id = self.current_local_schematisation.id
        self.schematisation = self.tc.fetch_schematisation(self.schematisation_id)
        current_wip_revision = self.current_local_schematisation.wip_revision
        latest_revision = (
            self.tc.fetch_schematisation_latest_revision(self.schematisation_id)
            if current_wip_revision.number > 0
            else None
        )
        latest_revision_number = latest_revision.number if latest_revision else 0
        if latest_revision_number != current_wip_revision.number:
            question = f"WIP revision number different than latest online revision ({latest_revision_number})"
            answer = self.communication.custom_ask(self, "Pick action", question, "Upload anyway?", "Cancel")
            if answer == "Cancel":
                return
        upload_wizard_dialog = UploadWizard(self.plugin_dock, self)
        upload_wizard_dialog.exec_()
        new_upload = upload_wizard_dialog.new_upload
        if not new_upload:
            return
        if not new_upload["upload_only"]:
            deletion_dlg = ModelDeletionDialog(self.plugin_dock, self)
            if len(deletion_dlg.threedi_models) >= self.MAX_SCHEMATISATION_MODELS:
                deletion_dlg.exec_()
                if len(deletion_dlg.threedi_models) >= self.MAX_SCHEMATISATION_MODELS:
                    self.communication.bar_warn("Uploading canceled...")
                    return
        self.add_upload_to_model(new_upload)

    def on_revision_committed(self):
        """Handling actions on successful revision commit."""
        self.plugin_dock.update_schematisation_view()

    def on_update_upload_progress(self, upload_row_number, task_name, task_progress, total_progress):
        """Handling actions on upload progress update."""
        self.upload_progresses[upload_row_number] = (task_name, task_progress, total_progress)
        if self.current_upload_row == upload_row_number:
            self.lbl_current_task.setText(task_name)
            self.pbar_current_task.setValue(task_progress)
            self.pbar_total_upload.setValue(total_progress)
            if task_progress == 100.0 and task_name != "DONE":
                success = True
                enriched_success_message = f"{task_name} ==> done"
                ended_task_row = (enriched_success_message, success)
                if upload_row_number not in self.ended_tasks:
                    self.ended_tasks[upload_row_number] = [ended_task_row]
                else:
                    upload_ended_tasks = self.ended_tasks[upload_row_number]
                    if ended_task_row not in upload_ended_tasks:
                        upload_ended_tasks.append(ended_task_row)
                    else:
                        return
                self.feedback_logger.log_info(enriched_success_message)

    def on_upload_finished_success(self, upload_row_number, msg):
        """Handling action on upload success."""
        item = self.tv_model.item(upload_row_number - 1, 3)
        item.setText(UploadStatus.SUCCESS.value)
        self.plugin_dock.communication.bar_info(msg, log_text_color=Qt.darkGreen)
        self.on_upload_context_change()

    def on_upload_failed(self, upload_row_number, error_message):
        """Handling action on upload failure."""
        item = self.tv_model.item(upload_row_number - 1, 3)
        item.setText(UploadStatus.FAILURE.value)
        self.plugin_dock.communication.bar_error(error_message, log_text_color=Qt.red)
        success = False
        failed_task_name = self.upload_progresses[self.current_upload_row][0]
        enriched_error_message = f"{failed_task_name} ==> failed\n{error_message}"
        failed_task_row = (enriched_error_message, success)
        if upload_row_number not in self.ended_tasks:
            self.ended_tasks[upload_row_number] = [failed_task_row]
        else:
            self.ended_tasks[upload_row_number].append(failed_task_row)
        self.feedback_logger.log_error(enriched_error_message)
        self.on_upload_context_change()
class DialogExportData(QDialog, DIALOG_UI):
    ValidExtensions = ['xtf', 'itf', 'gml', 'xml']
    current_row_schema = 0

    def __init__(self, iface, db, qgis_utils):
        QDialog.__init__(self)
        self.setupUi(self)
        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.db = db
        self.qgis_utils = qgis_utils
        self.base_configuration = BaseConfiguration()
        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self.type_combo_box.clear()
        self.type_combo_box.addItem(
            QCoreApplication.translate("DialogExportData",
                                       "PostgreSQL/PostGIS"), 'pg')
        self.type_combo_box.addItem(
            QCoreApplication.translate("DialogExportData", "GeoPackage"),
            'gpkg')
        self.type_combo_box.currentIndexChanged.connect(self.type_changed)
        self.type_changed()

        self.xtf_file_browse_button.clicked.connect(
            make_save_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate("DialogExportData",
                                                 "Save in XTF Transfer File"),
                file_filter=QCoreApplication.translate(
                    "DialogExportData",
                    "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)"
                ),
                extension='.xtf',
                extensions=['.' + ext for ext in self.ValidExtensions]))
        self.xtf_file_browse_button.clicked.connect(
            self.xtf_browser_opened_to_true)
        self.xtf_browser_was_opened = False

        self.validators = Validators()
        fileValidator = FileValidator(
            pattern=['*.' + ext for ext in self.ValidExtensions],
            allow_non_existing=True)
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogExportData",
                                       "[Name of the XTF to be created]"))
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(
            self.validators.validate_line_edits)
        self.xtf_file_line_edit.textChanged.connect(
            self.xtf_browser_opened_to_false)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        # PG
        self.db_connect_label.setToolTip(self.db.get_display_conn_string())
        self.db_connect_label.setText(self.db.dict_conn_params["database"])
        self.connection_setting_button.clicked.connect(self.show_settings)

        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogExportData",
                                       "Connection Settings"))

        # GPKG
        self.gpkg_file_line_edit.setPlaceholderText(
            QCoreApplication.translate(
                "DialogExportData", "[Name of the Geopackage to be created]"))
        self.gpkg_file_browse_button.clicked.connect(
            make_file_selector(self.gpkg_file_line_edit,
                               title=QCoreApplication.translate(
                                   "DialogExportData",
                                   "Open GeoPackage database file"),
                               file_filter=QCoreApplication.translate(
                                   "DialogExportData",
                                   "GeoPackage Database (*.gpkg)")))
        gpkgFileValidator = FileValidator(pattern='*.gpkg')
        self.gpkg_file_line_edit.setValidator(gpkgFileValidator)
        self.gpkg_file_line_edit.textChanged.connect(
            self.validators.validate_line_edits)
        self.gpkg_file_line_edit.textChanged.emit(
            self.gpkg_file_line_edit.text())

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogExportData", "Show log"))
        self.log_config.setFlat(True)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self.buttonBox.addButton(
            QCoreApplication.translate("DialogExportData", "Export data"),
            QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

    def showEvent(self, event):
        # update after create dialog
        self.update_schema_names_model()
        self.update_model_names(self.get_checked_schema())
        self.restore_configuration()

    def update_schema_names_model(self):
        res, msg = self.db.test_connection()
        schema_names = self.db._schema_names_list()

        if schema_names:
            for schema_name in schema_names:
                item = QListWidgetItem(schema_name['schema_name'])
                item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
                item.setCheckState(Qt.Unchecked)
                self.schema_names_list_widget.addItem(item)

            default_item = self.schema_names_list_widget.item(0)
            default_item.setCheckState(Qt.Checked)
        else:
            self.schema_names_list_widget.clear()
            self.export_models_list_view.setModel(QStandardItemModel())
            message_error = 'There are no schemes to export into the database. Please select another database.'
            self.txtStdout.setText(
                QCoreApplication.translate("DialogExportData", message_error))
            self.show_message(message_error, Qgis.Warning)

        self.schema_names_list_widget.currentRowChanged.connect(
            self.on_current_row_changed_schema_names)
        self.schema_names_list_widget.itemChanged.connect(
            self.on_itemchanged_schema_name)

    def on_itemchanged_schema_name(self, selected_item):

        # disconnect signal to do changes in the items
        self.schema_names_list_widget.itemChanged.disconnect(
            self.on_itemchanged_schema_name)

        for index in range(self.schema_names_list_widget.count()):
            item = self.schema_names_list_widget.item(index)
            item.setCheckState(Qt.Unchecked)
            item.setSelected(False)
            if item == selected_item:
                select_index = index

        item = self.schema_names_list_widget.item(select_index)
        item.setCheckState(Qt.Checked)
        item.setSelected(True)

        # Update list view with models name info
        schema_name = item.text()
        self.update_model_names(schema_name)

        # connect signal to check when the items change
        self.schema_names_list_widget.itemChanged.connect(
            self.on_itemchanged_schema_name)

    def on_current_row_changed_schema_names(self, current_row):
        for index in range(self.schema_names_list_widget.count()):
            item = self.schema_names_list_widget.item(index)
            item.setCheckState(Qt.Unchecked)

        item = self.schema_names_list_widget.item(current_row)

        if item:
            item.setCheckState(Qt.Checked)
            # Update list view with models name info
            schema_name = item.text()
            self.update_model_names(schema_name)

    def update_model_names(self, dbschema):
        self.export_models_qmodel = QStandardItemModel()

        db_models = None
        if self.type_combo_box.currentData() == 'gpkg':
            db_models = self.db.get_models()
        elif self.type_combo_box.currentData() == 'pg':
            db_models = self.db.get_models(dbschema) if dbschema else None

        if db_models:
            for db_model in db_models:
                regex = re.compile(r'(?:\{[^\}]*\}|\s)')
                for modelname in regex.split(db_model['modelname']):
                    if modelname and modelname not in DEFAULT_HIDDEN_MODELS:
                        item = QStandardItem(modelname.strip())
                        item.setCheckable(False)
                        item.setEditable(False)
                        self.export_models_qmodel.appendRow(item)

        self.export_models_list_view.setModel(self.export_models_qmodel)

    def get_checked_schema(self):
        checked_schema = None
        for index in range(self.schema_names_list_widget.count()):
            item = self.schema_names_list_widget.item(index)
            if item.checkState() == Qt.Checked:
                checked_schema = item.text()
                break
        return checked_schema

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.export_models_qmodel.rowCount()):
            item = self.export_models_qmodel.item(index)
            ili_models.append(item.text())
        return ili_models

    def show_settings(self):
        dlg = self.qgis_utils.get_settings_dialog()
        dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX)
        if dlg.exec_():
            self.db = self.qgis_utils.get_db_connection()
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self.db_connect_label.setText(self.db.dict_conn_params['database'])
            self.update_schema_names_model()

    def accepted(self):
        configuration = self.update_configuration()

        if not self.get_checked_schema():
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "You need to select a valid schema where to get the data from."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.connection_setting_button.setFocus()
            return

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid XTF file before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid schema to export. This schema does not have information to export."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        if not configuration.iliexportmodels:
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a model before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        if self.type_combo_box.currentData() == 'gpkg':
            if not configuration.dbfile or self.gpkg_file_line_edit.validator(
            ).validate(configuration.dbfile, 0)[0] != QValidator.Acceptable:
                message_error = QCoreApplication.translate(
                    "DialogExportData",
                    "Please set an existing database file before creating the project."
                )
                self.txtStdout.setText(message_error)
                self.show_message(message_error, Qgis.Warning)
                self.gpkg_file_line_edit.setFocus()
                return

        # If xtf browser was opened and the file exists, the user already chose
        # to overwrite the file
        if os.path.isfile(self.xtf_file_line_edit.text().strip()
                          ) and not self.xtf_browser_was_opened:
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Warning)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogExportData",
                    "{filename} already exists.\nDo you want to replace it?").
                format(filename=os.path.basename(
                    self.xtf_file_line_edit.text().strip())))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogExportData",
                                           "Save in XTF Transfer File"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg_box = self.msg.exec_()
            if msg_box == QMessageBox.No:
                return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()
            self.progress_bar.setValue(0)

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            exporter = iliexporter.Exporter()

            exporter.tool_name = 'ili2pg' if self.type_combo_box.currentData(
            ) == 'pg' else 'ili2gpkg'
            exporter.configuration = configuration

            self.save_configuration(configuration)

            exporter.stdout.connect(self.print_info)
            exporter.stderr.connect(self.on_stderr)
            exporter.process_started.connect(self.on_process_started)
            exporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if exporter.run() != iliexporter.Exporter.SUCCESS:
                    self.enable()
                    self.progress_bar.hide()
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogExportData",
                            "An error occurred when exporting the data. For more information see the log..."
                        ), Qgis.Warning)
                    return
            except JavaNotFoundError:
                # Set JAVA PATH
                get_java_path_dlg = DialogGetJavaPath()
                get_java_path_dlg.setModal(True)
                if get_java_path_dlg.exec_():
                    configuration = self.update_configuration()

                if not get_java_path_from_qgis_model_baker():
                    message_error_java = QCoreApplication.translate(
                        "DialogExportData",
                        """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again."""
                    )
                    self.txtStdout.setTextColor(QColor('#000000'))
                    self.txtStdout.clear()
                    self.txtStdout.setText(message_error_java)
                    self.show_message(message_error_java, Qgis.Warning)
                self.enable()
                self.progress_bar.hide()
                return

            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogExportData",
                    "Export of the data was successfully completed."),
                Qgis.Success)

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export',
            configuration.xtffile)
        settings.setValue('Asistente-LADM_COL/db_connection_source',
                          self.type_combo_box.currentData())
        settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

        if self.type_combo_box.currentData() == 'gpkg':
            settings.setValue(
                'Asistente-LADM_COL/QgisModelBaker/ili2gpkg/dbfile',
                configuration.dbfile)

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export'))
        self.type_combo_box.setCurrentIndex(
            self.type_combo_box.findData(
                settings.value('Asistente-LADM_COL/db_connection_source',
                               'pg')))
        self.type_changed()

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option by default use online model repository
        custom_model_is_checked = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            type=bool)
        if custom_model_is_checked:
            self.custom_model_directories = settings.value(
                'Asistente-LADM_COL/models/custom_models')

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        configuration = ExportConfiguration()

        if self.type_combo_box.currentData() == 'pg':
            # PostgreSQL specific options
            configuration.dbhost = self.db.dict_conn_params["host"]
            configuration.dbport = self.db.dict_conn_params["port"]
            configuration.dbusr = self.db.dict_conn_params["username"]
            configuration.database = self.db.dict_conn_params["database"]
            configuration.dbschema = self.get_checked_schema()
            configuration.dbpwd = self.db.dict_conn_params["password"]
        elif self.type_combo_box.currentData() == 'gpkg':
            configuration.dbfile = self.db.dict_conn_params["dbfile"]

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        java_path = get_java_path_from_qgis_model_baker()
        if java_path:
            self.base_configuration.java_path = java_path

        # Check custom model directories
        if QSettings().value(
                'Asistente-LADM_COL/models/custom_model_directories_is_checked',
                type=bool):
            if self.custom_model_directories is None:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.iliexportmodels = ';'.join(self.get_ili_models())
            configuration.ilimodels = ';'.join(self.get_ili_models())

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append(
            QCoreApplication.translate("DialogExportData",
                                       "Finished ({})").format(exit_code))
        if result == iliexporter.Exporter.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.enable()

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: process data...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(70)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(85)
            QCoreApplication.processEvents()

    def show_help(self):
        self.qgis_utils.show_help("export_data")

    def disable(self):
        self.type_combo_box.setEnabled(False)
        self.pg_config.setEnabled(False)
        self.ili_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.type_combo_box.setEnabled(True)
        self.pg_config.setEnabled(True)
        self.ili_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)

    def type_changed(self):
        self.progress_bar.hide()
        if self.type_combo_box.currentData() == 'pg':
            self.pg_config.show()
            self.gpkg_config.hide()
        elif self.type_combo_box.currentData() == 'gpkg':
            self.pg_config.hide()
            self.gpkg_config.show()

    def xtf_browser_opened_to_true(self):
        """
        Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = True

    def xtf_browser_opened_to_false(self):
        """
        Slot. Sets a flag to false to eventually ask a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = False
Example #15
0
class SchematisationLoad(uicls, basecls):
    """Dialog for local schematisation loading."""
    def __init__(self, plugin_dock, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.plugin_dock = plugin_dock
        self.working_dir = self.plugin_dock.plugin_settings.working_dir
        self.communication = self.plugin_dock.communication
        self.local_schematisations = list_local_schematisations(
            self.working_dir)
        self.tv_schematisations_model = QStandardItemModel()
        self.schematisations_tv.setModel(self.tv_schematisations_model)
        self.tv_revisions_model = QStandardItemModel()
        self.revisions_tv.setModel(self.tv_revisions_model)
        self.selected_local_schematisation = None
        self.pb_load.clicked.connect(self.load_local_schematisation)
        self.pb_cancel.clicked.connect(self.cancel_load_local_schematisation)
        self.schematisations_tv.selectionModel().selectionChanged.connect(
            self.populate_local_schematisation_revisions)
        self.revisions_tv.selectionModel().selectionChanged.connect(
            self.toggle_load_local_schematisation)
        self.populate_local_schematisations()

    def populate_local_schematisations(self):
        """Populate local schematisations."""
        self.tv_revisions_model.clear()
        self.tv_schematisations_model.clear()
        header = ["Schematisation name", "Schematisation ID", "Absolute path"]
        self.tv_schematisations_model.setHorizontalHeaderLabels(header)
        for schematisation_id, local_schematisation in self.local_schematisations.items(
        ):
            name_item = QStandardItem(local_schematisation.name)
            name_item.setData(local_schematisation, role=Qt.UserRole)
            id_item = QStandardItem(str(schematisation_id))
            dir_item = QStandardItem(local_schematisation.main_dir)
            self.tv_schematisations_model.appendRow(
                [name_item, id_item, dir_item])
        for i in range(len(header)):
            self.schematisations_tv.resizeColumnToContents(i)

    def populate_local_schematisation_revisions(self):
        """Populate local schematisation revisions."""
        self.tv_revisions_model.clear()
        header = ["Revision number", "Subdirectory"]
        self.tv_revisions_model.setHorizontalHeaderLabels(header)
        local_schematisation = self.get_selected_local_schematisation()
        wip_revision = local_schematisation.wip_revision
        if wip_revision is not None:
            number_item = QStandardItem(str(wip_revision.number))
            number_item.setData(wip_revision, role=Qt.UserRole)
            subdir_item = QStandardItem(wip_revision.sub_dir)
            self.tv_revisions_model.appendRow([number_item, subdir_item])
        for revision_number, local_revision in reversed(
                local_schematisation.revisions.items()):
            number_item = QStandardItem(str(revision_number))
            number_item.setData(local_revision, role=Qt.UserRole)
            subdir_item = QStandardItem(local_revision.sub_dir)
            self.tv_revisions_model.appendRow([number_item, subdir_item])
        for i in range(len(header)):
            self.schematisations_tv.resizeColumnToContents(i)
        if self.tv_revisions_model.rowCount() > 0:
            row_idx = self.tv_revisions_model.index(0, 0)
            self.revisions_tv.selectionModel().setCurrentIndex(
                row_idx, QItemSelectionModel.ClearAndSelect)
        self.toggle_load_local_schematisation()

    def toggle_load_local_schematisation(self):
        """Toggle load button if any schematisation revision is selected."""
        selection_model = self.revisions_tv.selectionModel()
        if selection_model.hasSelection():
            self.pb_load.setEnabled(True)
        else:
            self.pb_load.setDisabled(True)

    def get_selected_local_schematisation(self):
        """Get currently selected local schematisation."""
        index = self.schematisations_tv.currentIndex()
        if index.isValid():
            current_row = index.row()
            name_item = self.tv_schematisations_model.item(current_row, 0)
            local_schematisation = name_item.data(Qt.UserRole)
        else:
            local_schematisation = None
        return local_schematisation

    def get_selected_local_revision(self):
        """Get currently selected local revision."""
        index = self.revisions_tv.currentIndex()
        if index.isValid():
            current_row = index.row()
            name_item = self.tv_revisions_model.item(current_row, 0)
            local_revision = name_item.data(Qt.UserRole)
        else:
            local_revision = None
        return local_revision

    def load_local_schematisation(self):
        """Loading selected local schematisation."""
        local_schematisation = self.get_selected_local_schematisation()
        local_revision = self.get_selected_local_revision()
        if not isinstance(local_revision, WIPRevision):
            title = "Pick action"
            question = f"Replace WIP with data from the revision {local_revision.number}?"
            picked_action_name = self.communication.custom_ask(
                self, title, question, "Replace", "Cancel")
            if picked_action_name == "Replace":
                wip_revision = local_schematisation.set_wip_revision(
                    local_revision.number)
                replace_revision_data(local_revision, wip_revision)
            else:
                local_schematisation = None
        self.selected_local_schematisation = local_schematisation
        self.close()

    def cancel_load_local_schematisation(self):
        """Cancel local schematisation loading."""
        self.close()
Example #16
0
class AdjustmentDialogThresholds(QObject):

    COLOR_ERROR = QColor(224, 103, 103)
    COLOR_ATTENTION = QColor(237, 148, 76)
    COLOR_NEUTRAL = QColor(255, 255, 255)
    COLOR = {1: COLOR_NEUTRAL, 2: COLOR_ATTENTION, 3: COLOR_ERROR}

    sig_clickedRow = pyqtSignal(int)

    def __init__(self, parent, datasetSize):
        """
        :type parent: gui.adjustmentDialog.AdjustmentDialog
        """
        super().__init__()
        self.parent = parent
        self.tbl = self.parent.tableThresholds
        self.model = QStandardItemModel(datasetSize[0], datasetSize[1],
                                        self.tbl)
        self.initState = True
        self.thresholdExeeded = False
        self.tbl.setModel(self.model)
        self.tbl.resizeColumnsToContents()
        self.tbl.resizeRowsToContents()
        # Icons
        self.iconOk = QIcon()
        self.iconOk.addPixmap(
            QPixmap(":/plugins/SeilaplanPlugin/gui/icons/icon_green.png"),
            QIcon.Normal, QIcon.Off)
        self.iconErr = QIcon()
        self.iconErr.addPixmap(
            QPixmap(
                ":/plugins/SeilaplanPlugin/gui/icons/icon_exclamation.png"),
            QIcon.Normal, QIcon.Off)

        self.tbl.clicked.connect(self.onClick)

    def populate(self, header, dataset, valueColumn):
        self.model.setHorizontalHeaderLabels(header)
        self.tbl.hideColumn(5)

        # Insert data into cells
        for i, rowData in enumerate(dataset):
            for j, cellData in enumerate(rowData):
                if j == 0:
                    # Create clickable info button in first column
                    btnWidget = self.createInfoBtn(cellData)
                    self.tbl.setIndexWidget(self.model.index(i, j), btnWidget)
                    continue
                if j == 5 and isinstance(cellData, dict):
                    loclen = len(cellData['loc'])
                    if loclen > 0:
                        # Set background color for cells where threshold is
                        #  exceeded
                        color = self.COLOR[max(cellData['color'] or [1])]
                        self.colorBackground(i, valueColumn, color)
                    cellData = loclen
                item = QStandardItem(cellData)
                self.model.setItem(i, j, item)
                self.model.setData(self.model.index(i, j), cellData)

        # Adjust column widths
        self.tbl.resizeColumnsToContents()
        for idx in range(2, self.model.columnCount()):
            currSize = self.tbl.sizeHintForColumn(idx)
            self.tbl.setColumnWidth(idx, max(currSize, 100))
        self.tbl.setFocusPolicy(Qt.NoFocus)
        self.updateTabIcon()

    def updateData(self, row, col, newVal):
        # Update background color of new values
        if col == 5 and isinstance(newVal, dict):
            locLen = len(newVal['loc'])
            color = self.COLOR[max(newVal['color'] or [1])]
            self.colorBackground(row, 4, color)
            newVal = locLen
        # Update value itself
        self.model.setData(self.model.index(row, col), newVal)
        self.updateTabIcon()

        # Remove the background color from initially calculated
        # cable line data
        if self.initState:
            self.initState = False
            for row in range(self.model.rowCount()):
                self.colorBackground(row, 3, self.COLOR_NEUTRAL)

    def colorBackground(self, row, col, color):
        self.model.setData(self.model.index(row, col), QBrush(color),
                           Qt.BackgroundRole)

    def updateTabIcon(self):
        """ Updates icon of QTabWidget with an exclamation mark or check
        mark depending on presents of exceeded thresholds."""
        thresholdExceeded = False
        for i in range(0, self.model.rowCount()):
            if i == 2:
                # Dont check thresholds for 'Sattelkraft'
                continue
            data = self.model.data(self.model.index(i, 5))
            if data and data > 0:
                thresholdExceeded = True
                break
        if thresholdExceeded:
            self.parent.tabWidget.setTabIcon(2, self.iconErr)
        else:
            self.parent.tabWidget.setTabIcon(2, self.iconOk)

    def onClick(self, item):
        # Row is already selected
        if self.parent.selectedThdRow == item.row():
            # Deselect
            self.tbl.clearSelection()
        # Emit select signal
        self.sig_clickedRow.emit(item.row())

    def createInfoBtn(self, cellData):
        button = QPushButton('?')
        button.setMaximumSize(QSize(22, 22))
        # Fill info text into message box
        button.clicked.connect(
            lambda: QMessageBox.information(self.parent, cellData[
                'title'], cellData['message'], QMessageBox.Ok))
        cellWidget = QWidget()
        # Add layout to center button in cell
        layout = QHBoxLayout(cellWidget)
        layout.addWidget(button, 0, Qt.AlignCenter)
        layout.setAlignment(Qt.AlignCenter)
        cellWidget.setLayout(layout)
        return cellWidget
Example #17
0
class DHIS2DataFetcher:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        self.MSG_TITLE = self.tr('DHIS2 datafetcher')
        locale = QSettings().value('locale/userLocale')[0:2]
        self.info('Locale: "{}"'.format(locale))
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            '{}.qm'.format(locale))
        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)
        else:
            self.info('Locale niet gevonden: "{}"'.format(locale_path))

        # start a group/dir for all settings
        self.SETTINGS_GROUP = self.tr('dhis2datafetcher')
        QgsSettings().beginGroup(self.SETTINGS_GROUP, QgsSettings.Plugins)

        # Create the dialog (after translation) and keep reference
        self.dlg = DHIS2DataFetcherDialog(self.iface.mainWindow())

        self.dlg.cb_ou.currentIndexChanged.connect(self.cb_ou_changed)
        self.dlg.cb_pe.currentIndexChanged.connect(self.cb_pe_changed)
        self.dlg.cb_dx.currentIndexChanged.connect(self.cb_dx_changed)
        self.dlg.cb_level.currentIndexChanged.connect(self.cb_level_changed)

        self.dlg.btn_load_geodata.clicked.connect(self.load_geodata_in_layer)
        self.dlg.btn_new_dataset.clicked.connect(self.new_dataset)

        # Replace the placeholder in the dialog with a QgsAuthConfigSelect widget
        self.dlg.cmb_profile_select.close()  # this apparently also removes the widget??
        self.dlg.cmb_profile_select = QgsAuthConfigSelect()
        self.dlg.gridLayout.addWidget(self.dlg.cmb_profile_select, 0, 1, 1, 1)  # row, col, #rows, #cols
        self.dlg.cmb_profile_select.selectedConfigIdChanged.connect(self.selectAuthConfig)

        # Declare instance attributes
        self.actions = []
        self.menu = QMenu(self.tr(u'KIT - DHIS2 Data Fetcher'))
        self.iface.pluginMenu().addMenu(self.menu)
        self.menu.setIcon(QIcon(':/plugins/DHIS2DataFetcher/icon_kit.png'))

        self.toolbar = self.iface.addToolBar(u'DHIS2DataFetcher')
        self.toolbar.setObjectName(u'DHIS2DataFetcher')

        self.api_url = None  # url as defined by user in authorisation profile
        self.auth_id = None  # authorisation id to be used in nam creation AND vectorlayer creation uri

        self.gui_inited = False
        self.nam = None  # created in gui during authorisation profile choice

        self.ou_items = []
        self.pe_items = []
        self.dx_items = []

        # current demo site
        self.analytics_url = 'https://play.dhis2.org/2.28/api/'
        self.username = '******'
        self.password = '******'
        self.level = 2

        # connect to the iface.projectRead signal to be able to refresh data in a project with a dhis2 layer
        self.iface.projectRead.connect(self.update_dhis2_project)

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'KIT - DHIS2 Data Fetcher'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar
        self.dlg = None
        QgsSettings().endGroup()
        self.iface.projectRead.disconnect(self.update_dhis2_project)

    def msg(self, msg=''):
        self.iface.messageBar().pushMessage(self.MSG_TITLE, msg)

    def info(self, msg=''):
        QgsMessageLog.logMessage('{}'.format(msg), self.MSG_TITLE, Qgis.Info)

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/DHIS2DataFetcher/icon_kit.png'
        self.add_action(
            icon_path,
            text=self.tr(u'KIT - Fetch DHIS2 Data'),
            callback=self.show_dialog,
            parent=self.iface.mainWindow())

        # help menu
        icon_path = ':/plugins/DHIS2DataFetcher/icon_kit.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Code and (preliminary) Documentation'),
            callback=self.show_help,
            add_to_toolbar=False,
            parent=self.iface.mainWindow())

    def show_help(self):
        #QDesktopServices.openUrl(QUrl("file:" + os.path.join(os.path.dirname(__file__), "help/html", "index.html"))
        QDesktopServices.openUrl(QUrl("https://github.com/rduivenvoorde/kit_dhis2_data_fetcher/"))

    def initDropdowns(self):

        # ou = Organisational Units
        self.ou_model = QStandardItemModel()
        self.pe_model = QStandardItemModel()
        self.dx_model = QStandardItemModel()

        # easy way to add ALL organisationUnits
        jsono = self.get_json('{}organisationUnits.json?paging=false&level={}'.format(self.api_url, self.level))
        if jsono:
            self.ou_model.appendRow([QStandardItem("ALL"), QStandardItem("ALL")])
            for item in jsono['organisationUnits']:
                display_name = item['displayName']
                ou_id = item['id']
                #self.info('{} - {}'.format(ou_id, display_name))
                self.ou_model.appendRow([QStandardItem(display_name), QStandardItem(ou_id)])
            self.dlg.cb_ou.setModel(self.ou_model)
        else:
            self.gui_inited = False
            return False

        # dx = indicators and data elements
        # indicators
        jsono = self.get_json('{}indicators.json?paging=false&level={}'.format(self.api_url, self.level))
        if jsono:
            for item in jsono['indicators']:
                display_name = item['displayName']
                ou_id = item['id']
                #self.info('{} - {}'.format(ou_id, display_name))
                self.dx_model.appendRow([QStandardItem(display_name), QStandardItem(ou_id)])
        else:
            self.gui_inited = False
            return False

        # dataElements
        jsono = self.get_json('{}dataElements.json?paging=false&level={}'.format(self.api_url, self.level))
        if jsono:
            for item in jsono['dataElements']:
                display_name = item['displayName']
                ou_id = item['id']
                #self.info('{} - {}'.format(ou_id, display_name))
                self.dx_model.appendRow([QStandardItem(display_name), QStandardItem(ou_id)])
            self.dlg.cb_dx.setModel(self.dx_model)
        else:
            self.gui_inited = False
            return False

        for pe in ['2018', '2017', '2016', '2015', 'LAST_YEAR', 'LAST_5_YEARS',
                   'THIS_MONTH', 'LAST_MONTH', 'LAST_3_MONTHS', 'MONTHS_THIS_YEAR', 'LAST_12_MONTHS']:
            self.pe_model.appendRow([QStandardItem(pe), QStandardItem(pe)])
        self.dlg.cb_pe.setModel(self.pe_model)

        self.ou_items = []
        self.pe_items = []
        self.dx_items = []

        self.dlg.cb_ou.setCurrentIndex(-1)
        self.dlg.cb_dx.setCurrentIndex(-1)
        self.dlg.cb_pe.setCurrentIndex(-1)

        self.gui_inited = True
        self.create_url()
        self.info('Finish INIT dropdowns')
        return True

    def get_json(self, url):
        jsono = {}
        try:
            self.info(url)
            (response, content) = self.nam.request(url)
            jsono = json.loads(content.decode('utf-8'))
        except Exception as e:
            self.msg(self.tr('Problem retrieving data from: {}'.format(url)))
            self.info(self.tr('Problem retrieving data from: {}'.format(url)))
            QMessageBox.warning(self.iface.mainWindow(), self.MSG_TITLE,
                                self.tr('Problem retrieving data from:\n{}'
                                '\nTest this url in browser to check if the api service is available,\nOR'
                                '\nChoose or define another service configuration.'.format(url)),
                   QMessageBox.Ok, QMessageBox.Ok)
            return False
        return jsono

    def load_geodata_in_layer(self):
        self.info('Loading level {} geodata'.format(self.level))
        url = "{}organisationUnits.geojson?paging=false&level={} authcfg='{}'".format(self.api_url, self.level, self.auth_id)
        geojson_layer = QgsVectorLayer(url, 'Level {} organisationUnits'.format(self.level), 'ogr')
        if geojson_layer.isValid():
            QgsProject.instance().addMapLayer(geojson_layer)
        else:
            self.info('Problem loading: {}'.format(url))

    def new_dataset(self):
        #self.info('Clean dataset url')
        # by setting another level, the url is cleaned
        self.cb_level_changed(0)

    def cb_ou_changed(self, index):
        #self.info('ou index change: {}'.format(index))
        if index < 0:
            return
        ou_id = self.ou_model.index(index, 1).data()
        #self.info('ou: {} {} {}'.format(index, ou_id, self.ou_model.index(index, 0).data()))
        if ou_id == 'ALL':
            # start with a clean sheet first:
            self.ou_items = []
            for idx in range(0, self.ou_model.rowCount()-1):
                self.ou_items.append(self.ou_model.index(idx, 1).data())
        elif ou_id in self.ou_items:
            self.ou_items.remove(ou_id)
        else:
            self.ou_items.append(ou_id)
        self.create_url()

    def cb_pe_changed(self, index):
        #self.info('pe index change: {}'.format(index))
        if index < 0:
            return
        pe_id = self.pe_model.index(index, 1).data()
        #self.info('Selected pe: {}'.format(pe_id))  # id
        if pe_id in self.pe_items:
            self.pe_items.remove(pe_id)
        else:
            self.pe_items.append(pe_id)
        self.create_url()

    def cb_dx_changed(self, index):
        #self.info('dx index change: {}'.format(index))
        if index < 0:
            return
        dx_id = self.dx_model.index(index, 1).data()
        #self.info('Selected dx: {} {} {}'.format(index, dx_id, self.dx_model.index(index, 0).data()))  # displayName
        if dx_id in self.dx_items:
            self.dx_items.remove(dx_id)
        else:
            self.dx_items.append(dx_id)
        self.create_url()

    def cb_level_changed(self, index):
        # redo dropdowns to the Level chossen
        self.gui_inited = False
        self.level = self.dlg.cb_level.currentText()
        #self.info('Level change to {}'.format(self.level))
        self.info('initDropdowns in cb_level_changed')
        if self.initDropdowns():
            self.create_url()
            self.dlg.grp_api.setEnabled(True)
        else:
            self.dlg.grp_api.setEnabled(False)

    def create_url(self):
        #self.info('Updating analytics url')
        url = '{}analytics.json?dimension=dx:{}&dimension=pe:{}&dimension=ou:{}&level={}'\
            .format(self.api_url, ';'.join(self.dx_items), ';'.join(self.pe_items), ';'.join(self.ou_items), self.level)
        self.dlg.le_url.setText(url)
        self.analytics_url = url
        if len(self.dx_items) == 0 or len(self.pe_items) == 0 or len(self.ou_items) == 0:
            return False
        return True

    def show_dialog(self):
        """

        :return:
        """
        self.dlg.show()
        # show the dialog
        # but FIRST check if user recently used a config_id
        self.dlg.cmb_profile_select.setConfigId(QgsSettings().value('last_conf_id', ''))

        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            if self.dlg.cmb_profile_select.configId() is '':
                self.msg(self.tr('No profile found, please choose or create profile first'))
                return
            if self.create_url() is False:
                self.msg(self.tr('Missing information, select an option from all dropdowns first'))
                return
            # ALWAYS grab url from dialog, as it is possible that user copied changed something there
            self.analytics_url = self.dlg.le_url.text()
            self.json2features(self.analytics_url)
        else:
            # Cancel was clicked
            pass

    def update_dhis2_project(self):
        # now go over layers and check if they have a dhis2_url property
        p = QgsProject.instance()
        for lname in p.mapLayers():
            lyr = p.mapLayer(lname)
            url = lyr.customProperty('dhis2_url', '')
            if len(url) > 0:
                #self.info('OK url !!')
                self.info(url)
                # if so: fetch fresh data, but reuse layer
                self.json2features(url, lyr)

    def json2features(self, url, data_layer=None):
        try:
            (response, content) = self.nam.request(url, method="GET")
        except RequestsException as e:
            self.info('ERROR: {}'.format(e))
            return

        jsons = content.decode('utf-8')
        jsono = json.loads(jsons)

        #print(json.dumps(jsono, sort_keys=True, indent=4))
        #print(jsono['height'])

        # creating memory layer with uri:
        # https://qgis.org/api/qgsmemoryproviderutils_8cpp_source.html
        if data_layer is None:
            data_layer = QgsVectorLayer('none', 'DHIS2 data', 'memory')

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.String))
        fields.append(QgsField('name', QVariant.String))

        metadata_items = jsono['metaData']['items']

        # create as much fields as there are pe_dx combinations
        # eg: 2017_birth, 2016_birth, 2017_measels, 2016_measels
        for pe in jsono['metaData']['dimensions']['pe']:
            for dx in jsono['metaData']['dimensions']['dx']:
                #field_alias = '{} {} ({})'.format(pe, metadata_items[dx]['name'], dx)
                field_alias = '{} {}'.format(pe, metadata_items[dx]['name'])
                field = QgsField('{}_{}'.format(pe, dx), QVariant.Double, comment=field_alias)
                field.setAlias(field_alias)
                fields.append(field)

        #self.info('Fields: {}'.format(fields))

        # clean up first
        data_layer.dataProvider().deleteAttributes(data_layer.dataProvider().attributeIndexes())
        data_layer.updateFields()

        # set new attributes
        data_layer.dataProvider().addAttributes(fields)
        data_layer.updateFields()

        # array with all features
        features = []
        # map which maps the every feature to its OrganisationalUnit
        feature_map = {}
        # create a feature for every ou/OrganisationUnit
        # AND make sure it has the pe_dx combination fields
        for ou in jsono['metaData']['dimensions']['ou']:
            #print(ou)
            f = QgsFeature()
            # set fields
            f.setFields(fields)
            # set id and name of the feature
            f.setAttribute('id', ou)
            f.setAttribute('name', jsono['metaData']['items'][ou]['name'])
            # append feature to features array and feature map
            features.append(f)
            feature_map[ou] = f

        # dynamic?
        # currently the order in which they are in the url is below
        dx_idx = 0
        pe_idx = 1
        ou_idx = 2
        value_idx = 3

        # now every cell in the table has a 'row in the json data', with dx, pe, ou and value
        for row in jsono['rows']:
            # pick feature based on OrganisationalUnit-key from the feature_map
            f = feature_map[row[ou_idx]]
            # attribute key is created from pe_dx string
            attr = '{}_{}'.format(row[pe_idx], row[dx_idx])
            # Births attended by skilled health personnel (estimated pregancies)
            f.setAttribute(attr, row[value_idx])

        data_layer.dataProvider().addFeatures(features)

        # add it to the project
        QgsProject.instance().addMapLayer(data_layer)
        # 'save' the data url of the layer into the project properties
        # https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/settings.html
        data_layer.setCustomProperty("dhis2_url", url)


    def selectAuthConfig(self, conf_id):
        self.info('"{}" selected as conf_id'.format(conf_id))
        QgsSettings().setValue('last_conf_id', conf_id)
        if conf_id is '':
            self.dlg.grp_api.setEnabled(False)
        else:
            auth_man = QgsApplication.authManager()
            uri = auth_man.availableAuthMethodConfigs()[conf_id].uri().strip()
            if uri.startswith('http'):
                # make sure it ends with /
                if not uri.endswith('/'):
                    uri = uri + '/'
                self.api_url = uri
                self.auth_id = conf_id
                # Set authid to use to 'dhis2ap' which has api url: https://play.dhis2.org/2.29/api/
                self.info("Set authid to use to '{}' which has api url: {}".format(self.auth_id, self.api_url))
                # note: user has to create an authenticaton configuration with id 'self.auth_id' to authorize the HTTP requests
                self.nam = NetworkAccessManager(authid=self.auth_id, exception_class=RequestsException, debug=False)
                self.info('initDropdowns in selectAuthConfig')
                if self.initDropdowns():
                    self.dlg.grp_api.setEnabled(True)
                else:
                    self.dlg.grp_api.setEnabled(False)
            else:
                self.info('Uri should start with "http", now it is: "{}"'.format(uri))

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('DHIS2DataFetcher', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            # self.iface.addPluginToMenu(
            #     self.menu,
            #     action)
            self.menu.addAction(action)

        self.actions.append(action)

        return action
Example #18
0
class Dialog(QDialog, Ui_attr2BUGS):
    def __init__(self, iface, ml):
        """Constructor for the dialog.
        
        Args:
          iface: QgsInterface instance.
        """

        QDialog.__init__(self, iface.mainWindow())

        self.setupUi(self)
        self.setWindowTitle('Attributes to BUGS')

        self.ml = ml

        self.ids = []
        self.polynum = self.ml.featureCount()

        self.model = QStandardItemModel(0, 1)
        self.listView.setModel(self.model)
        self.model.setHeaderData(0, Qt.Horizontal, 'Field')

        provider = self.ml.dataProvider()
        fields = provider.fields()
        for f in fields:
            item = QStandardItem(f.name())
            item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
            item.setData(Qt.Unchecked, Qt.CheckStateRole)
            self.model.appendRow(item)

        self.control()

        self.pushButton.clicked.connect(self.konv)
        self.pushButton_2.clicked.connect(self.close)
        self.pushButton_3.clicked.connect(self.save)

    def konv(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.plainTextEdit.clear()

        mod = min(self.ids)

        provider = self.ml.dataProvider()
        fields = provider.fields()
        lst = '#data\nlist('

        for row in range(0, self.model.rowCount()):
            it = self.model.itemFromIndex(self.model.index(row, 0))
            if it.checkState() == 2:
                fld = str(self.model.itemData(self.model.index(row, 0))[0])
                var = fld + '=c('
                ft = fields[row].type()
                if ft == 10:
                    for ne in range(mod, self.polynum + mod):
                        feat = QgsFeature()
                        fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
                        if fiter.nextFeature(feat):
                            u = feat.attribute(fld)
                            var += "'%s'," % u
                    var = var[:-2] + "')"
                else:
                    for ne in range(mod, self.polynum + mod):
                        feat = QgsFeature()
                        fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
                        if fiter.nextFeature(feat):
                            u = feat.attribute(fld)
                            var += "%s," % u
                    var = var[:-1] + ')'
                lst += var + ', '
        lst += ')'
        lst = lst.replace('), )', '))')
        self.plainTextEdit.appendPlainText(lst)

        QApplication.restoreOverrideCursor()

    def control(self):
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        #self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, self.polynum))
        ne = 0
        while feats.nextFeature(feat):
            ne += 1
            #self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            self.ids.append(feat.id())

    def save(self):
        fileName, _ = QFileDialog.getSaveFileName(self, caption='Save As...')
        try:
            file = QFile(fileName + '.txt')
            file.open(QIODevice.WriteOnly | QIODevice.Text)
            out = QTextStream(file)
            out << self.plainTextEdit.toPlainText()
            out.flush()
            file.close()
            self.close()
            return True
        except IOError:
            return False
Example #19
0
class DialogExportData(QDialog, DIALOG_UI):
    ValidExtensions = ['xtf', 'itf', 'gml', 'xml']
    current_row_schema = 0

    def __init__(self, iface, qgis_utils, conn_manager):
        QDialog.__init__(self)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.conn_manager = conn_manager
        self.db = self.conn_manager.get_db_connector_from_source()
        self.qgis_utils = qgis_utils
        self.base_configuration = BaseConfiguration()
        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._conf_db = ConfigDbSupported()
        self._params = None
        self._current_db = None

        self.xtf_file_browse_button.clicked.connect(
            make_save_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate("DialogExportData",
                                                 "Save in XTF Transfer File"),
                file_filter=QCoreApplication.translate(
                    "DialogExportData",
                    "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)"
                ),
                extension='.xtf',
                extensions=['.' + ext for ext in self.ValidExtensions]))
        self.xtf_file_browse_button.clicked.connect(
            self.xtf_browser_opened_to_true)
        self.xtf_browser_was_opened = False

        self.validators = Validators()
        fileValidator = FileValidator(
            pattern=['*.' + ext for ext in self.ValidExtensions],
            allow_non_existing=True)
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogExportData",
                                       "[Name of the XTF to be created]"))
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(
            self.validators.validate_line_edits)
        self.xtf_file_line_edit.textChanged.connect(
            self.xtf_browser_opened_to_false)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        self.connection_setting_button.clicked.connect(self.show_settings)

        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogExportData",
                                       "Connection Settings"))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogExportData", "Show log"))
        self.log_config.setFlat(True)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            QCoreApplication.translate("DialogExportData", "Export data"),
            QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.update_model_names()
        self.restore_configuration()

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogExportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_model_names(self):
        self.export_models_qmodel = QStandardItemModel()

        db_models = None
        db_models = self.db.get_models()

        if db_models:
            for db_model in db_models:
                regex = re.compile(r'(?:\{[^\}]*\}|\s)')
                for modelname in regex.split(db_model['modelname']):
                    if modelname and modelname not in DEFAULT_HIDDEN_MODELS:
                        item = QStandardItem(modelname.strip())
                        item.setCheckable(False)
                        item.setEditable(False)
                        self.export_models_qmodel.appendRow(item)

        self.export_models_list_view.setModel(self.export_models_qmodel)

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.export_models_qmodel.rowCount()):
            item = self.export_models_qmodel.item(index)
            ili_models.append(item.text())
        return ili_models

    def show_settings(self):
        dlg = SettingsDialog(qgis_utils=self.qgis_utils,
                             conn_manager=self.conn_manager)

        # Connect signals (DBUtils, QgisUtils)
        dlg.db_connection_changed.connect(
            self.conn_manager.db_connection_changed)
        dlg.db_connection_changed.connect(
            self.qgis_utils.cache_layers_and_relations)
        dlg.organization_tools_changed.connect(
            self.qgis_utils.organization_tools_changed)

        dlg.set_action_type(EnumDbActionType.EXPORT)
        dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX)
        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_model_names()
            self.update_connection_info()

    def accepted(self):
        configuration = self.update_configuration()

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid XTF file before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid schema to export. This schema does not have information to export."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        if not configuration.iliexportmodels:
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a model before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        # If xtf browser was opened and the file exists, the user already chose
        # to overwrite the file
        if os.path.isfile(self.xtf_file_line_edit.text().strip()
                          ) and not self.xtf_browser_was_opened:
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Warning)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogExportData",
                    "{filename} already exists.\nDo you want to replace it?").
                format(filename=os.path.basename(
                    self.xtf_file_line_edit.text().strip())))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogExportData",
                                           "Save in XTF Transfer File"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg_box = self.msg.exec_()
            if msg_box == QMessageBox.No:
                return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()
            self.progress_bar.setValue(0)

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            exporter = iliexporter.Exporter()

            item_db = self._conf_db.get_db_items()[self.db.mode]

            exporter.tool = item_db.get_mbaker_db_ili_mode()
            exporter.configuration = configuration

            self.save_configuration(configuration)

            exporter.stdout.connect(self.print_info)
            exporter.stderr.connect(self.on_stderr)
            exporter.process_started.connect(self.on_process_started)
            exporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if exporter.run() != iliexporter.Exporter.SUCCESS:
                    self.enable()
                    self.progress_bar.hide()
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogExportData",
                            "An error occurred when exporting the data. For more information see the log..."
                        ), Qgis.Warning)
                    return
            except JavaNotFoundError:
                # Set JAVA PATH
                get_java_path_dlg = GetJavaPathDialog()
                get_java_path_dlg.setModal(True)
                if get_java_path_dlg.exec_():
                    configuration = self.update_configuration()

                if not get_java_path_from_qgis_model_baker():
                    message_error_java = QCoreApplication.translate(
                        "DialogExportData",
                        """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again."""
                    )
                    self.txtStdout.setTextColor(QColor('#000000'))
                    self.txtStdout.clear()
                    self.txtStdout.setText(message_error_java)
                    self.show_message(message_error_java, Qgis.Warning)
                self.enable()
                self.progress_bar.hide()
                return

            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogExportData",
                    "Export of the data was successfully completed."),
                Qgis.Success)

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export',
            configuration.xtffile)
        settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option by default use online model repository
        custom_model_is_checked = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            type=bool)
        if custom_model_is_checked:
            self.custom_model_directories = settings.value(
                'Asistente-LADM_COL/models/custom_models')

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        item_db = self._conf_db.get_db_items()[self.db.mode]

        configuration = ExportConfiguration()
        item_db.set_db_configuration_params(self.db.dict_conn_params,
                                            configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        java_path = get_java_path_from_qgis_model_baker()
        if java_path:
            self.base_configuration.java_path = java_path

        # Check custom model directories
        if QSettings().value(
                'Asistente-LADM_COL/models/custom_model_directories_is_checked',
                type=bool):
            if self.custom_model_directories is None:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.iliexportmodels = ';'.join(self.get_ili_models())
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append(
            QCoreApplication.translate("DialogExportData",
                                       "Finished ({})").format(exit_code))
        if result == iliexporter.Exporter.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.enable()

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: process data...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(70)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(85)
            QCoreApplication.processEvents()

    def show_help(self):
        self.qgis_utils.show_help("export_data")

    def disable(self):
        self.db_config.setEnabled(False)
        self.ili_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.db_config.setEnabled(True)
        self.ili_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)

    def xtf_browser_opened_to_true(self):
        """
        Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = True

    def xtf_browser_opened_to_false(self):
        """
        Slot. Sets a flag to false to eventually ask a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = False
Example #20
0
class DialogImportData(QDialog, DIALOG_UI):
    open_dlg_import_schema = pyqtSignal(dict)  # dict with key-value params
    BUTTON_NAME_IMPORT_DATA = QCoreApplication.translate(
        "DialogImportData", "Import data")
    BUTTON_NAME_GO_TO_CREATE_STRUCTURE = QCoreApplication.translate(
        "DialogImportData", "Go to Create Structure...")

    def __init__(self,
                 iface,
                 conn_manager,
                 context,
                 link_to_import_schema=True,
                 parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.conn_manager = conn_manager
        self.db_source = context.get_db_sources()[0]
        self.link_to_import_schema = link_to_import_schema
        self.db = self.conn_manager.get_db_connector_from_source(
            self.db_source)
        self.base_configuration = BaseConfiguration()
        self.logger = Logger()
        self.app = AppInterface()
        self.__ladmcol_models = LADMColModelRegistry()

        self.java_dependency = JavaDependency()
        self.java_dependency.download_dependency_completed.connect(
            self.download_java_complete)
        self.java_dependency.download_dependency_progress_changed.connect(
            self.download_java_progress_change)

        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._dbs_supported = ConfigDBsSupported()
        self._running_tool = False

        # There may be 1 case where we need to emit a db_connection_changed from the Import Data dialog:
        #   1) Connection Settings was opened and the DB conn was changed.
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings

        # Similarly, we could call a refresh on layers and relations cache in 1 case:
        #   1) If the ID dialog was called for the COLLECTED source: opening Connection Settings and changing the DB
        #      connection.
        self._schedule_layers_and_relations_refresh = False

        # We need bar definition above calling clear_messages
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.xtf_file_browse_button.clicked.connect(
            make_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate(
                    "DialogImportData", "Open Transfer or Catalog File"),
                file_filter=QCoreApplication.translate(
                    "DialogImportData",
                    'Transfer File (*.xtf *.itf);;Catalogue File (*.xml *.xls *.xlsx)'
                )))

        self.validators = Validators()
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogImportData",
                                       "[Name of the XTF to be imported]"))
        fileValidator = FileValidator(pattern=['*.xtf', '*.itf', '*.xml'])
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(self.update_import_models)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        # db
        self.connection_setting_button.clicked.connect(self.show_settings)
        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogImportData",
                                       "Connection Settings"))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogImportData", "Show log"))

        self.buttonBox.accepted.disconnect()
        self.buttonBox.clicked.connect(self.accepted_import_data)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            self.BUTTON_NAME_IMPORT_DATA, QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.restore_configuration()

    def accepted_import_data(self, button):
        if self.buttonBox.buttonRole(button) == QDialogButtonBox.AcceptRole:
            if button.text() == self.BUTTON_NAME_IMPORT_DATA:
                self.accepted()
            elif button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE:
                self.close()  # Close import data dialog
                self.open_dlg_import_schema.emit({
                    'selected_models':
                    self.get_ili_models()
                })  # Emit signal to open import schema dialog

    def reject(self):
        if self._running_tool:
            QMessageBox.information(
                self, QCoreApplication.translate("DialogImportData",
                                                 "Warning"),
                QCoreApplication.translate(
                    "DialogImportData",
                    "The Import Data tool is still running. Please wait until it finishes."
                ))
        else:
            self.close_dialog()

    def close_dialog(self):
        """
        We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that
        unload the plugin, destroying dialogs and thus, leading to crashes.
        """
        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.connect(
                self.app.core.cache_layers_and_relations)

        if self._db_was_changed:
            # If the db was changed, it implies it complies with ladm_col, hence the second parameter
            self.conn_manager.db_connection_changed.emit(
                self.db, True, self.db_source)

        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.disconnect(
                self.app.core.cache_layers_and_relations)

        self.logger.info(__name__, "Dialog closed.")
        self.done(QDialog.Accepted)

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogImportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_import_models(self):
        self.clear_messages()
        error_msg = None

        if not self.xtf_file_line_edit.text().strip():
            color = '#ffd356'  # Light orange
            self.import_models_qmodel = QStandardItemModel()
            self.import_models_list_view.setModel(self.import_models_qmodel)
        else:
            if os.path.isfile(self.xtf_file_line_edit.text().strip()):
                color = '#fff'  # White

                self.import_models_qmodel = QStandardItemModel()
                model_names = get_models_from_xtf(
                    self.xtf_file_line_edit.text().strip())

                for model in self.__ladmcol_models.supported_models():
                    if not model.hidden() and model.full_name() in model_names:
                        item = QStandardItem(model.full_alias())
                        item.setData(model.full_name(), Qt.UserRole)
                        item.setCheckable(False)
                        item.setEditable(False)
                        self.import_models_qmodel.appendRow(item)

                if self.import_models_qmodel.rowCount() > 0:
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
                else:
                    error_msg = QCoreApplication.translate(
                        "DialogImportData",
                        "No models were found in the XTF. Is it a valid file?")
                    color = '#ffd356'  # Light orange
                    self.import_models_qmodel = QStandardItemModel()
                    self.import_models_list_view.setModel(
                        self.import_models_qmodel)
            else:
                error_msg = QCoreApplication.translate(
                    "DialogImportData", "Please set a valid XTF file")
                color = '#ffd356'  # Light orange
                self.import_models_qmodel = QStandardItemModel()
                self.import_models_list_view.setModel(
                    self.import_models_qmodel)
        self.xtf_file_line_edit.setStyleSheet(
            'QLineEdit {{ background-color: {} }}'.format(color))

        if error_msg:
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.import_models_qmodel.rowCount()):
            item = self.import_models_qmodel.item(index)
            ili_models.append(item.data(Qt.UserRole))
        return ili_models

    def show_settings(self):
        # We only need those tabs related to Model Baker/ili2db operations
        dlg = SettingsDialog(self.conn_manager, parent=self)
        dlg.setWindowTitle(
            QCoreApplication.translate("DialogImportData",
                                       "Target DB Connection Settings"))
        dlg.show_tip(
            QCoreApplication.translate(
                "DialogImportData",
                "Configure where do you want the XTF data to be imported."))
        dlg.set_db_source(self.db_source)
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])

        # Connect signals (DBUtils, Core)
        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            self._schedule_layers_and_relations_refresh = True

        dlg.set_action_type(EnumDbActionType.IMPORT)

        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_connection_info()

    def db_connection_changed(self, db, ladm_col_db, db_source):
        self._db_was_changed = True
        self.clear_messages()

    def accepted(self):
        self._running_tool = True
        self.txtStdout.clear()
        self.progress_bar.setValue(0)
        self.bar.clearWidgets()

        if not os.path.isfile(self.xtf_file_line_edit.text().strip()):
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "Please set a valid XTF file before importing data. XTF file does not exist."
            )
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        java_home_set = self.java_dependency.set_java_home()
        if not java_home_set:
            message_java = QCoreApplication.translate(
                "DialogImportData",
                """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION)
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()
            self.txtStdout.setText(message_java)
            self.java_dependency.get_java_on_demand()
            self.disable()
            return

        configuration = self.update_configuration()

        if configuration.disable_validation:  # If data validation at import is disabled, we ask for confirmation
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Question)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogImportData",
                    "Are you sure you want to import your data without validation?"
                ))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogImportData",
                                           "Import XTF without validation?"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            res = self.msg.exec_()
            if res == QMessageBox.No:
                self._running_tool = False
                return

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "Please set a valid XTF before importing data.")
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            self._running_tool = False
            error_msg = QCoreApplication.translate(
                "DialogImportData",
                "The selected XTF file does not have information according to the LADM-COL model to import."
            )
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

        # Get list of models present in the XTF file, in the DB and in the list of required models (by the plugin)
        ili_models = set([ili_model for ili_model in self.get_ili_models()])

        supported_models_in_ili = set([
            m.full_name() for m in self.__ladmcol_models.supported_models()
        ]).intersection(ili_models)

        if not supported_models_in_ili:
            self._running_tool = False
            error_msg = QCoreApplication.translate("DialogImportData",
                                                   "The selected XTF file does not have data from any LADM-COL model supported by the LADM-COL Assistant. " \
                                                   "Therefore, you cannot import it! These are the models supported:\n\n * {}").format(" \n * ".join([m.full_alias() for m in self.__ladmcol_models.supported_models()]))
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.import_models_list_view.setFocus()
            return

        db_models = set(self.db.get_models())
        suggested_models = sorted(ili_models.difference(db_models))

        if not ili_models.issubset(db_models):
            self._running_tool = False
            error_msg = QCoreApplication.translate("DialogImportData",
                                                   "IMPORT ERROR: The XTF file to import does not have the same models as the target database schema. " \
                                                   "Please create a schema that also includes the following missing modules:\n\n * {}").format(
                " \n * ".join(suggested_models))
            self.txtStdout.clear()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.setText(error_msg)
            self.show_message(error_msg, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()

            # button is removed to define order in GUI
            for button in self.buttonBox.buttons():
                if button.text() == self.BUTTON_NAME_IMPORT_DATA:
                    self.buttonBox.removeButton(button)

            # Check if button was previously added
            self.remove_create_structure_button()

            if self.link_to_import_schema:
                self.buttonBox.addButton(
                    self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE,
                    QDialogButtonBox.AcceptRole).setStyleSheet(
                        "color: #aa2222;")
            self.buttonBox.addButton(self.BUTTON_NAME_IMPORT_DATA,
                                     QDialogButtonBox.AcceptRole)

            return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            dataImporter = iliimporter.Importer(dataImport=True)

            db_factory = self._dbs_supported.get_db_factory(self.db.engine)

            dataImporter.tool = db_factory.get_model_baker_db_ili_mode()
            dataImporter.configuration = configuration

            self.save_configuration(configuration)

            dataImporter.stdout.connect(self.print_info)
            dataImporter.stderr.connect(self.on_stderr)
            dataImporter.process_started.connect(self.on_process_started)
            dataImporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if dataImporter.run() != iliimporter.Importer.SUCCESS:
                    self._running_tool = False
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogImportData",
                            "An error occurred when importing the data. For more information see the log..."
                        ), Qgis.Warning)
                    return
            except JavaNotFoundError:
                self._running_tool = False
                error_msg_java = QCoreApplication.translate(
                    "DialogImportData",
                    "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
                ).format(JAVA_REQUIRED_VERSION)
                self.txtStdout.setTextColor(QColor('#000000'))
                self.txtStdout.clear()
                self.txtStdout.setText(error_msg_java)
                self.show_message(error_msg_java, Qgis.Warning)
                return

            self._running_tool = False
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogImportData",
                    "Import of the data was successfully completed"),
                Qgis.Success)

    def download_java_complete(self):
        self.accepted()

    def download_java_progress_change(self, progress):
        self.progress_bar.setValue(progress / 2)
        if (progress % 20) == 0:
            self.txtStdout.append('...')

    def remove_create_structure_button(self):
        for button in self.buttonBox.buttons():
            if button.text() == self.BUTTON_NAME_GO_TO_CREATE_STRUCTURE:
                self.buttonBox.removeButton(button)

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import',
            configuration.xtffile)
        settings.setValue('Asistente-LADM-COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_import'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM-COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option  by default use online model repository
        self.use_local_models = settings.value(
            'Asistente-LADM-COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        if self.use_local_models:
            self.custom_model_directories = settings.value(
                'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR)

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        db_factory = self._dbs_supported.get_db_factory(self.db.engine)

        configuration = ImportDataConfiguration()
        db_factory.set_ili2db_configuration_params(self.db.dict_conn_params,
                                                   configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        configuration.delete_data = False

        configuration.srs_auth = QSettings().value(
            'Asistente-LADM-COL/QgisModelBaker/srs_auth', DEFAULT_SRS_AUTH,
            str)
        configuration.srs_code = QSettings().value(
            'Asistente-LADM-COL/QgisModelBaker/srs_code',
            int(DEFAULT_SRS_CODE), int)
        configuration.inheritance = ILI2DBNames.DEFAULT_INHERITANCE
        configuration.create_basket_col = ILI2DBNames.CREATE_BASKET_COL
        configuration.create_import_tid = ILI2DBNames.CREATE_IMPORT_TID
        configuration.stroke_arcs = ILI2DBNames.STROKE_ARCS
        configuration.with_importtid = True

        full_java_exe_path = JavaDependency.get_full_java_exe_path()
        if full_java_exe_path:
            self.base_configuration.java_path = full_java_exe_path

        # User could have changed the default values
        self.use_local_models = QSettings().value(
            'Asistente-LADM-COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        self.custom_model_directories = QSettings().value(
            'Asistente-LADM-COL/models/custom_models', DEFAULT_MODELS_DIR)

        # Check custom model directories
        if self.use_local_models:
            if not self.custom_model_directories:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM-COL/models/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append('Finished ({})'.format(exit_code))
        if result == iliimporter.Importer.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.show_message(
                QCoreApplication.translate("DialogImportData",
                                           "Error when importing data"),
                Qgis.Warning)

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: create table structure...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(60)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(80)
            QCoreApplication.processEvents()

    def clear_messages(self):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.txtStdout.clear()  # Clear previous log messages
        self.progress_bar.setValue(0)  # Initialize progress bar

    def show_help(self):
        show_plugin_help("import_data")

    def disable(self):
        self.source_config.setEnabled(False)
        self.target_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.source_config.setEnabled(True)
        self.target_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        if level == Qgis.Warning:
            self.enable()

        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage("Asistente LADM-COL", message, level, duration=0)
class RuimtelijkePlannen(object):
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface and the mapCanvas
        self.iface = iface
        self.mapcanvas = iface.mapCanvas()
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        if not QSettings().value('locale/userLocale'):
            locale = QSettings().value('locale/globalLocale')[0:2]
        else:
            locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'RuimtelijkePlannen_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Ruimtelijke Plannen')
        self.toolbar = self.iface.addToolBar(u'RuimtelijkePlannen')
        self.toolbar.setObjectName(u'RuimtelijkePlannen')

        self.nam = networkaccessmanager.NetworkAccessManager()
        self.project = QgsProject.instance()

        # initialize styles folder
        self.settings = QSettings()
        self.available_styles_folder = os.path.join(self.plugin_dir, 'styles')
        self.map_style = self.settings.value("RuimtelijkePlannen/map_style",
                                             "print")
        self.map_style_folder = os.path.join(self.available_styles_folder,
                                             self.map_style)
        if not os.path.isdir(self.map_style_folder):
            self.map_style = "print"
            self.map_style_folder = os.path.join(self.available_styles_folder,
                                                 self.map_style)

    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """

        return QCoreApplication.translate('RuimtelijkePlannen', message)

    def add_action(self,
                   dialog,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        if dialog:
            self.dlg = dialog

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToWebMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        self.dlg = RuimtelijkePlannenDialog()
        self.settings_dlg = RuimtelijkePlannenSettingsDialog()

        self.add_action(dialog=None,
                        icon_path=':/plugins/RuimtelijkePlannen/help.png',
                        text=self.tr(u'Help'),
                        callback=self.open_help,
                        parent=self.iface.mainWindow(),
                        add_to_toolbar=False)

        self.add_action(dialog=None,
                        icon_path=':/plugins/RuimtelijkePlannen/settings.png',
                        text=self.tr(u'Settings'),
                        callback=self.run_settings,
                        parent=self.iface.mainWindow(),
                        add_to_toolbar=False)

        # add a button to click in map and find bestemmingsplannen
        self.action = self.add_action(
            dialog=None,
            icon_path=':/plugins/RuimtelijkePlannen/icon.png',
            text=self.tr(u'Click map query RuimtelijkePlannen'),
            callback=self.activate_tool,
            add_to_menu=True,
            status_tip=self.tr(u'Click map query RuimtelijkePlannen'),
            parent=self.iface.mainWindow())
        self.action.setCheckable(True)
        # add it to the same group as the pan/zoom tools
        self.iface.actionPan().actionGroup().addAction(self.action)
        self.xytool = GetPointTool(self.mapcanvas, self.getRPplannenByPoint)

        self.toolbarSearch = QLineEdit()
        self.toolbarSearch.setMaximumWidth(200)
        self.toolbarSearch.setAlignment(QtCore.Qt.AlignLeft)
        self.toolbarSearch.setPlaceholderText("NL.IMRO.")
        self.toolbar.addWidget(self.toolbarSearch)
        self.toolbarSearch.returnPressed.connect(self.addRPplanFromToolbar)

        self.proxyModel = QSortFilterProxyModel()
        self.sourceModel = QStandardItemModel()
        self.proxyModel.setSourceModel(self.sourceModel)

        self.dlg.treeView_results.setModel(self.proxyModel)
        self.dlg.treeView_results.setEditTriggers(
            QAbstractItemView.NoEditTriggers)
        self.dlg.treeView_results.setSortingEnabled(True)
        self.dlg.treeView_results.doubleClicked.connect(self.loadRPplan)

        # have an info icon at hand for keuzehulp
        self.infoIcon = QIcon(':/plugins/RuimtelijkePlannen/info.png')

        # have the right crs at hand
        self.rp_crs = QgsCoordinateReferenceSystem(28992)

        ## urls to RuimtelijkePlannen
        ## this one is the traditional one
        # self.rp_search_url = "https://www.ruimtelijkeplannen.nl/web-roo/rest/search"
        # self.rp_search_id_resource = "/plan/id/"
        # self.rp_search_xy_resource = "/plannen/xy/"
        ## this one is new and has more metadata with the "keuzehulp"!
        self.rp_search_url = "https://www.ruimtelijkeplannen.nl/plannenservice"
        self.rp_search_id_resource = "/plannen/identificatie/"
        self.rp_search_xy_resource = "/plannen/xy/"

        self.rp_wfs_url = "https://afnemers.ruimtelijkeplannen.nl/afnemers2012/services"

        # the following lists of plantypes comes from:
        # https://www.ruimtelijkeplannen.nl/viewer/planoverzicht
        self.rp_supported_planTypes = [\
            'aanwijzingsbesluit',
            'beheersverordening',
            'bestemmingsplan',
            'exploitatieplan',
            'gemeentelijk plan; bestemmingsplan artikel 10',
            'gemeentelijk plan; uitwerkingsplan artikel 11',
            'gemeentelijk plan; voorbereidingsbesluit',
            'gemeentelijk plan; wijzigingsplan artikel 11',
            'gemeentelijke visie; overig',
            'gerechtelijke uitspraak',
            'inpassingsplan',
            'omgevingsvergunning',
            'projectbesluit',
            'reactieve aanwijzing',
            'tijdelijke ontheffing buitenplans',
            'uitwerkingsplan',
            'voorbereidingsbesluit',
            'wijzigingsplan'
            ]
        self.rp_not_supported_plantypes = [\
            # these are the plan types which can have multiple maps

            'amvb',
            'provinciale verordening',
            'regeling',
            'structuurvisie'
            ]

        # layer action on RuimtelijkePlannen layers to show links to text
        self.rp_layer_action = QgsAction(QgsAction.GenericPython,
                                         'Open text link(s) in browser ',
                                         layer_action, "", False, "",
                                         {'Feature', 'Canvas'})

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginWebMenu(self.tr(u'&Ruimtelijke Plannen'),
                                           action)
            self.iface.removeToolBarIcon(action)
        del self.toolbar

    def open_help(self):
        '''
        Method to open the help pages
        '''

        QDesktopServices().openUrl(
            QUrl.fromLocalFile(
                os.path.join("file://", self.plugin_dir, 'help/build/html',
                             'index.html')))

    def run_settings(self):
        '''
        method showing the settings dialog and act on the results
        '''

        # we do this on opening this setting, so the user can easily add
        # new styles wthout reloading the plugin.
        available_styles = next(os.walk(self.available_styles_folder))[1]
        self.settings_dlg.styleComboBox.clear()
        self.settings_dlg.styleComboBox.addItems(available_styles)

        index = self.settings_dlg.styleComboBox.findText(self.map_style)
        self.settings_dlg.styleComboBox.setCurrentIndex(index)

        self.settings_dlg.show()
        result = self.settings_dlg.exec_()
        if result:
            map_style_folder = os.path.join(
                self.available_styles_folder,
                self.settings_dlg.styleComboBox.currentText())
            if os.path.isdir(map_style_folder):
                self.map_style = self.settings_dlg.styleComboBox.currentText()
                self.map_style_folder = os.path.join(
                    self.available_styles_folder, self.map_style)
                self.settings.setValue("RuimtelijkePlannen/map_style",
                                       self.map_style)
            else:
                self.iface.messageBar().pushMessage(
                    'Warning',
                    self.
                    tr(u"Selected style folder not found. See message log for details."
                       ),
                    level=Qgis.Warning)
                QgsMessageLog.logMessage(u'Style folder not found: ' + \
                    os.path.join(self.available_styles_folder, self.map_style),
                    'RuimtelijkePlannen')

    def activate_tool(self):
        self.mapcanvas.setMapTool(self.xytool)

    def addRPplanFromToolbar(self):
        '''slot for toolbarSearch'''
        self.addFromIdn(self.toolbarSearch.text())

    def addFromIdn(self, search_string):
        '''adds plan from idn'''
        self.toolbarSearch.clear()
        url = self.rp_search_url + self.rp_search_id_resource + search_string
        QgsMessageLog.logMessage(u'Query: ' + str(url), 'RuimtelijkePlannen')
        QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        (response, content) = self.nam.request(url)
        self.rp = json.loads(content.decode("utf-8"))

        QApplication.restoreOverrideCursor()
        if "ErrorType" in self.rp \
        or ('aantal' in self.rp and self.rp['aantal'] == 0):
            self.iface.messageBar().pushMessage("Error",
                                                self.tr(u'Plan not found.'),
                                                level=Qgis.Critical)
            return
        if 'resultaat' in self.rp:
            # we are using the keuzeHulp
            self.rp = self.rp['plannen'][0]
        if not self.rp["typePlan"] in self.rp_supported_planTypes:
            self.iface.messageBar().pushMessage(
                "Error",
                self.tr(u'Plan type not supported.'),
                level=Qgis.Critical)
        else:
            self.addRPplan_WFS(plangebied=search_string,
                               plantype=self.rp["typePlan"])

    def getWfsLayer(self, service, typename, wfs_filter):
        params = {
            'service': 'WFS',
            'version': '1.1.0',
            'request': 'GetFeature',
            'typename': typename,
            'filter': wfs_filter,
            'srsname': 'EPSG:28992',
        }
        if not service[-1] == '?':
            service += '?'
        uri = service + urllib.parse.unquote(urllib.parse.urlencode(params))
        layername = typename
        vlayer = QgsVectorLayer(uri, layername, "WFS")
        return vlayer

    def addRPplan_WFS(self, plangebied, plantype):
        '''adds a plan as a WFS layer'''

        QgsMessageLog.logMessage(u'Adding plan with IDN "%s" and plantype "%s"'\
            % (plangebied,plantype), 'RuimtelijkePlannen')

        # add a layergroup to insert the layers in
        bpGroup = QgsProject.instance().layerTreeRoot().insertGroup(
            0, plangebied)

        service = self.rp_wfs_url
        wfs_filter = '"plangebied"=\'' + plangebied + '\''
        plan = rp_plan(plantype)
        for layer in plan.layers:
            vlayer = self.getWfsLayer(service, layer['name'], wfs_filter)
            if vlayer.isValid():
                if 'qml' in layer:
                    layerQml = os.path.join(self.map_style_folder,
                                            layer['qml'])
                    if os.path.exists(layerQml):
                        vlayer.loadNamedStyle(layerQml)
                    else:
                        self.iface.messageBar().pushMessage(
                            'Warning',
                            self.
                            tr(u"Style not found. See message log for details."
                               ),
                            level=Qgis.Warning)
                        QgsMessageLog.logMessage(u'Style file not found: ' + \
                            str(layerQml), 'RuimtelijkePlannen')
                if layer['text_action']:
                    vlayer.actions().addAction(self.rp_layer_action)
                self.project.addMapLayer(vlayer, False)
                bpGroup.insertChildNode(-1, QgsLayerTreeLayer(vlayer))
            else:
                self.iface.messageBar().pushMessage(
                    'Warning',
                    self.tr(u"Invalid layer: ") + layer['name'],
                    level=Qgis.Warning)

        vlayer.selectAll()
        canvas = self.iface.mapCanvas()
        canvas.zoomToSelected(vlayer)
        vlayer.removeSelection()

    def loadRPplan(self, index):
        '''slot for row in search results widget'''
        self.dlg.hide()
        self.addRPplan_WFS(plangebied=index.sibling(index.row(), 1).data(),
                           plantype=index.sibling(index.row(), 3).data())

    def addSourceRow(self, nr, plan, keuze_hulp=None):
        '''adds plan to search results widget'''
        if plan["typePlan"] in self.rp_supported_planTypes:
            nr = QStandardItem(str(nr + 1))
            planId = QStandardItem(plan["identificatie"])
            if keuze_hulp:
                planStatus = QStandardItem(self.infoIcon, plan["planStatus"])
                f = planStatus.font()
                f.setUnderline(True)
                planStatus.setFont(f)
                planStatus.setToolTip(
                    '%s: %s' %
                    (keuze_hulp["positieTekst"], keuze_hulp["keuzeHulpTekst"]))
            else:
                planStatus = QStandardItem(plan["planStatus"])
            planNaam = QStandardItem(plan["naam"])
            planType = QStandardItem(plan["typePlan"])
            #planGebiedType = QStandardItem(plan["planGebiedType"])
            # not yet(?) in use by this plugin
            self.sourceModel.appendRow(
                [nr, planId, planStatus, planType, planNaam])

    def addSourceRowFromKeuzeHulp(self, nr, plan_en_keuze):
        '''adds plan to search results widget'''
        keuze_hulp = plan_en_keuze['keuzeHulp']
        plan = plan_en_keuze['planDto']

        self.addSourceRow(nr, plan, keuze_hulp)

    def getRPplannenByPoint(self, event):
        '''Queries ruimtelijkeplannen by point and shows results on widget.'''

        xform = QgsCoordinateTransform(
            self.mapcanvas.mapSettings().destinationCrs(), self.rp_crs,
            QgsProject.instance())
        xy = xform.transform(
            QgsPointXY(
                self.mapcanvas.getCoordinateTransform().toMapCoordinates(
                    event.pos().x(),
                    event.pos().y())))

        self.sourceModel.clear()
        url = self.rp_search_url + self.rp_search_xy_resource + str(
            xy.x()) + "/" + str(xy.y())
        QgsMessageLog.logMessage(u'Query: ' + str(url), 'RuimtelijkePlannen')

        QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        (response, content) = self.nam.request(url)
        self.rp = json.loads(content.decode("utf-8"))

        if "ErrorType" in self.rp:
            self.iface.messageBar().pushMessage("Error",
                                                self.tr(u'No Plans found.') +
                                                self.rp["ErrorDescription"],
                                                level=Qgis.Critical)
            QApplication.restoreOverrideCursor()
            return

        if 'plannen' in self.rp:
            for nr, plan in enumerate(self.rp["plannen"]):
                self.addSourceRow(nr, plan)
        else:
            for nr, plan_en_keuze in enumerate(self.rp["keuzeHulpPlannen"]):
                self.addSourceRowFromKeuzeHulp(nr, plan_en_keuze)

        if not self.sourceModel.rowCount():
            QApplication.restoreOverrideCursor()
            return

        self.sourceModel.setHeaderData(0, QtCore.Qt.Horizontal, "Nr")
        self.sourceModel.setHeaderData(1, QtCore.Qt.Horizontal,
                                       "Identification")
        self.sourceModel.horizontalHeaderItem(1).setTextAlignment(
            QtCore.Qt.AlignLeft)
        self.sourceModel.setHeaderData(2, QtCore.Qt.Horizontal, "Status")
        self.sourceModel.setHeaderData(3, QtCore.Qt.Horizontal, "Type")
        self.sourceModel.setHeaderData(4, QtCore.Qt.Horizontal, "Name")
        self.dlg.treeView_results.setColumnHidden(0, True)
        self.dlg.treeView_results.resizeColumnsToContents()
        self.dlg.treeView_results.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.Stretch)
        self.dlg.treeView_results.setSelectionMode(1)
        self.dlg.treeView_results.selectRow(0)
        # sort on order of the keuzehulp, instead of status.
        #self.dlg.treeView_results.sortByColumn(2, QtCore.Qt.AscendingOrder)
        self.dlg.treeView_results.sortByColumn(0, QtCore.Qt.AscendingOrder)
        QApplication.restoreOverrideCursor()
        self.dlg.show()
Example #22
0
class ListWidget(EditorWidget):
    widgettype = 'List'

    def __init__(self, *args, **kwargs):
        super(ListWidget, self).__init__(*args, **kwargs)
        self.listmodel = QStandardItemModel()
        self._bindvalue = None

    def createWidget(self, parent):
        return QComboBox(parent)

    def _buildfromlist(self, widget, listconfig):
        items = listconfig['items']
        for item in items:
            parts = item.split(';')
            data = parts[0]
            try:
                desc = parts[1]
            except IndexError:
                desc = data

            try:
                path = parts[2]
                path = path.strip()
                icon = QIcon(path)
            except:
                icon = QIcon()

            item = QStandardItem(desc)
            item.setData(data, Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def _buildfromlayer(self, widget, layerconfig):
        layername = layerconfig['layer']
        keyfield = layerconfig['key']
        valuefield = layerconfig['value']
        filterexp = layerconfig.get('filter', None)

        try:
            layer = utils.layer_by_name(layername)
        except IndexError:
            roam.utils.warning(
                "Can't find layer {} in project".format(layername))
            return

        keyfieldindex = layer.fields().lookupField(keyfield)
        valuefieldindex = layer.fields().lookupField(valuefield)
        if keyfieldindex == -1 or valuefieldindex == -1:
            roam.utils.warning(f"Can't find key or value column for widget "
                               f"Id: {self.id} "
                               f"Layer: {layername} "
                               f"Key: {keyfield} - {keyfieldindex} "
                               f"Value: {valuefield} - {valuefieldindex} ")
            return

        if self.allownulls:
            item = QStandardItem('(no selection)')
            item.setData(None, Qt.UserRole)
            self.listmodel.appendRow(item)

        fields = [keyfieldindex, valuefieldindex]
        iconfieldindex = layer.fields().lookupField('icon')
        if iconfieldindex > -1:
            fields.append("icon")

        if not filterexp and valuefieldindex == keyfieldindex and iconfieldindex == -1:
            values = layer.uniqueValues(keyfieldindex)
            values = sorted(values)
            for value in values:
                value = nullconvert(value)
                item = QStandardItem(value)
                item.setData(value, Qt.UserRole)
                self.listmodel.appendRow(item)
            return

        features = roam.api.utils.search_layer(layer,
                                               filterexp,
                                               fields,
                                               with_geometry=False)
        # Sort the fields based on value field
        features = sorted(features, key=lambda f: f[keyfield])
        for feature in features:
            keyvalue = nullconvert(feature[keyfieldindex])
            valuvalue = nullconvert(feature[valuefield])
            try:
                path = feature["icon"]
                icon = QIcon(path)
            except KeyError:
                icon = QIcon()

            item = QStandardItem(keyvalue)
            item.setData(str(valuvalue), Qt.UserRole)
            item.setIcon(icon)
            self.listmodel.appendRow(item)

    def initWidget(self, widget, config):
        if widget.isEditable():
            widget.editTextChanged.connect(self.emitvaluechanged)

        widget.currentIndexChanged.connect(self.emitvaluechanged)
        widget.setModel(self.listmodel)
        widget.showPopup = self.showpopup
        widget.setIconSize(QSize(24, 24))
        widget.setStyleSheet(
            "QComboBox::drop-down {border-width: 0px;} QComboBox::down-arrow {image: url(noimg); border-width: 0px;}"
        )
        widget.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)

    def showpopup(self):
        if self.listmodel.rowCount() == 0:
            return

        self.largewidgetrequest.emit(
            BigListWidget, self.widget.currentIndex(), self._biglistitem,
            dict(model=self.listmodel, label=self.labeltext))

    def updatefromconfig(self):
        super(ListWidget, self).updatefromconfig()

        self.listmodel.clear()
        if 'list' in self.config:
            listconfig = self.config['list']
            self._buildfromlist(self.widget, listconfig)
        elif 'layer' in self.config:
            layerconfig = self.config['layer']
            self._buildfromlayer(self.widget, layerconfig)

        super(ListWidget, self).endupdatefromconfig()

    @property
    def allownulls(self):
        return self.config.get('allownull', False)

    def validate(self, *args):
        if (not self.widget.currentText() == ''
                and not self.widget.currentText() == "(no selection)"):
            return True
        else:
            return False

    def _biglistitem(self, index):
        self.widget.setCurrentIndex(index.row())

    def setvalue(self, value):
        self._bindvalue = value
        index = self.widget.findData(value)
        self.widget.setCurrentIndex(index)
        if index == -1 and self.widget.isEditable():
            if value is None and not self.config['allownull']:
                return

            self.widget.addItem(str(value))
            index = self.widget.count() - 1
            self.widget.setCurrentIndex(index)

    def value(self):
        index = self.widget.currentIndex()
        value = self.widget.itemData(index)
        text = self.widget.currentText()
        if value is None and self.widget.isEditable(
        ) and not text == '(no selection)':
            return self.widget.currentText()

        return value
class DialogExportData(QDialog, DIALOG_UI):
    on_result = pyqtSignal(
        bool)  # whether the tool was run successfully or not

    ValidExtensions = ['xtf', 'itf', 'gml', 'xml']
    current_row_schema = 0

    def __init__(self, iface, qgis_utils, conn_manager, context):
        QDialog.__init__(self)
        self.setupUi(self)

        QgsGui.instance().enableAutoGeometryRestore(self)
        self.iface = iface
        self.conn_manager = conn_manager
        self.db_source = context.get_db_sources()[0]
        self.db = self.conn_manager.get_db_connector_from_source(
            self.db_source)
        self.qgis_utils = qgis_utils
        self.logger = Logger()

        self.java_utils = JavaUtils()
        self.java_utils.download_java_completed.connect(
            self.download_java_complete)
        self.java_utils.download_java_progress_changed.connect(
            self.download_java_progress_change)

        self.base_configuration = BaseConfiguration()
        self.ilicache = IliCache(self.base_configuration)
        self.ilicache.refresh()

        self._dbs_supported = ConfigDbSupported()
        self._running_tool = False

        # There may be 1 case where we need to emit a db_connection_changed from the Export Data dialog:
        #   1) Connection Settings was opened and the DB conn was changed.
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings

        # Similarly, we could call a refresh on layers and relations cache in 1 case:
        #   1) If the ED dialog was called for the COLLECTED source: opening Connection Settings and changing the DB
        #      connection.
        self._schedule_layers_and_relations_refresh = False

        # We need bar definition above calling clear_messages
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.xtf_file_browse_button.clicked.connect(
            make_save_file_selector(
                self.xtf_file_line_edit,
                title=QCoreApplication.translate("DialogExportData",
                                                 "Save in XTF Transfer File"),
                file_filter=QCoreApplication.translate(
                    "DialogExportData",
                    "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)"
                ),
                extension='.xtf',
                extensions=['.' + ext for ext in self.ValidExtensions]))
        self.xtf_file_browse_button.clicked.connect(
            self.xtf_browser_opened_to_true)
        self.xtf_browser_was_opened = False

        self.validators = Validators()
        fileValidator = FileValidator(
            pattern=['*.' + ext for ext in self.ValidExtensions],
            allow_non_existing=True)
        self.xtf_file_line_edit.setPlaceholderText(
            QCoreApplication.translate("DialogExportData",
                                       "[Name of the XTF to be created]"))
        self.xtf_file_line_edit.setValidator(fileValidator)
        self.xtf_file_line_edit.textChanged.connect(
            self.validators.validate_line_edits)
        self.xtf_file_line_edit.textChanged.connect(
            self.xtf_browser_opened_to_false)
        self.xtf_file_line_edit.textChanged.emit(
            self.xtf_file_line_edit.text())

        self.connection_setting_button.clicked.connect(self.show_settings)

        self.connection_setting_button.setText(
            QCoreApplication.translate("DialogExportData",
                                       "Connection Settings"))

        # LOG
        self.log_config.setTitle(
            QCoreApplication.translate("DialogExportData", "Show log"))
        self.log_config.setFlat(True)

        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.clear()
        self.buttonBox.addButton(QDialogButtonBox.Cancel)
        self._accept_button = self.buttonBox.addButton(
            QCoreApplication.translate("DialogExportData", "Export data"),
            QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton(QDialogButtonBox.Help)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.update_connection_info()
        self.update_model_names()
        self.restore_configuration()

    def update_connection_info(self):
        db_description = self.db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(self.db.get_display_conn_string())
            self._accept_button.setEnabled(True)
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("DialogExportData",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')
            self._accept_button.setEnabled(False)

    def update_model_names(self):
        self.export_models_qmodel = QStandardItemModel()

        model_names = self.db.get_models()

        if model_names:
            for model_name in model_names:
                if model_name not in LADMNames.DEFAULT_HIDDEN_MODELS:
                    item = QStandardItem(model_name)
                    item.setCheckable(False)
                    item.setEditable(False)
                    self.export_models_qmodel.appendRow(item)

        self.export_models_list_view.setModel(self.export_models_qmodel)

    def reject(self):
        if self._running_tool:
            QMessageBox.information(
                self, QCoreApplication.translate("DialogExportData",
                                                 "Warning"),
                QCoreApplication.translate(
                    "DialogExportData",
                    "The Export Data tool is still running. Please wait until it finishes."
                ))
        else:
            self.close_dialog()

    def close_dialog(self):
        """
        We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that
        unload the plugin, destroying dialogs and thus, leading to crashes.
        """
        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.connect(
                self.qgis_utils.cache_layers_and_relations)

        if self._db_was_changed:
            # If the db was changed, it implies it complies with ladm_col, hence the second parameter
            self.conn_manager.db_connection_changed.emit(
                self.db, True, self.db_source)

        if self._schedule_layers_and_relations_refresh:
            self.conn_manager.db_connection_changed.disconnect(
                self.qgis_utils.cache_layers_and_relations)

        self.logger.info(__name__, "Dialog closed.")
        self.done(QDialog.Accepted)

    def get_ili_models(self):
        ili_models = list()
        for index in range(self.export_models_qmodel.rowCount()):
            item = self.export_models_qmodel.item(index)
            ili_models.append(item.text())
        return ili_models

    def show_settings(self):
        # We only need those tabs related to Model Baker/ili2db operations
        dlg = SettingsDialog(qgis_utils=self.qgis_utils,
                             conn_manager=self.conn_manager)
        dlg.set_db_source(self.db_source)
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])

        # Connect signals (DBUtils, QgisUtils)
        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            self._schedule_layers_and_relations_refresh = True

        dlg.set_action_type(EnumDbActionType.EXPORT)

        if dlg.exec_():
            self.db = dlg.get_db_connection()
            self.update_model_names()
            self.update_connection_info()

    def db_connection_changed(self, db, ladm_col_db, db_source):
        self._db_was_changed = True
        self.clear_messages()

    def accepted(self):
        self._running_tool = True
        self.bar.clearWidgets()

        java_home_set = self.java_utils.set_java_home()
        if not java_home_set:
            message_java = QCoreApplication.translate(
                "DialogExportData",
                """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION)
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()
            self.txtStdout.setText(message_java)
            self.java_utils.get_java_on_demand()
            self.disable()
            return

        configuration = self.update_configuration()

        if not self.xtf_file_line_edit.validator().validate(
                configuration.xtffile, 0)[0] == QValidator.Acceptable:
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid XTF file before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.xtf_file_line_edit.setFocus()
            return

        if not self.get_ili_models():
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a valid schema to export. This schema does not have information to export."
            )
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        if not configuration.iliexportmodels:
            self._running_tool = False
            message_error = QCoreApplication.translate(
                "DialogExportData",
                "Please set a model before exporting data.")
            self.txtStdout.setText(message_error)
            self.show_message(message_error, Qgis.Warning)
            self.export_models_list_view.setFocus()
            return

        # If xtf browser was opened and the file exists, the user already chose
        # to overwrite the file
        if os.path.isfile(self.xtf_file_line_edit.text().strip()
                          ) and not self.xtf_browser_was_opened:
            self.msg = QMessageBox()
            self.msg.setIcon(QMessageBox.Warning)
            self.msg.setText(
                QCoreApplication.translate(
                    "DialogExportData",
                    "{filename} already exists.\nDo you want to replace it?").
                format(filename=os.path.basename(
                    self.xtf_file_line_edit.text().strip())))
            self.msg.setWindowTitle(
                QCoreApplication.translate("DialogExportData",
                                           "Save in XTF Transfer File"))
            self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg_box = self.msg.exec_()
            if msg_box == QMessageBox.No:
                self._running_tool = False
                return

        with OverrideCursor(Qt.WaitCursor):
            self.progress_bar.show()

            self.disable()
            self.txtStdout.setTextColor(QColor('#000000'))
            self.txtStdout.clear()

            exporter = iliexporter.Exporter()

            db_factory = self._dbs_supported.get_db_factory(self.db.engine)

            exporter.tool = db_factory.get_mbaker_db_ili_mode()
            exporter.configuration = configuration

            self.save_configuration(configuration)

            exporter.stdout.connect(self.print_info)
            exporter.stderr.connect(self.on_stderr)
            exporter.process_started.connect(self.on_process_started)
            exporter.process_finished.connect(self.on_process_finished)

            self.progress_bar.setValue(25)

            try:
                if exporter.run() != iliexporter.Exporter.SUCCESS:
                    self._running_tool = False
                    self.show_message(
                        QCoreApplication.translate(
                            "DialogExportData",
                            "An error occurred when exporting the data. For more information see the log..."
                        ), Qgis.Warning)
                    self.on_result.emit(
                        False
                    )  # Inform other classes that the execution was not successful
                    return
            except JavaNotFoundError:
                self._running_tool = False
                message_error_java = QCoreApplication.translate(
                    "DialogExportData",
                    "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
                ).format(JAVA_REQUIRED_VERSION)
                self.txtStdout.setTextColor(QColor('#000000'))
                self.txtStdout.clear()
                self.txtStdout.setText(message_error_java)
                self.show_message(message_error_java, Qgis.Warning)
                return

            self._running_tool = False
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
            self.progress_bar.setValue(100)
            self.show_message(
                QCoreApplication.translate(
                    "DialogExportData",
                    "Export of the data was successfully completed."),
                Qgis.Success)
            self.on_result.emit(
                True)  # Inform other classes that the execution was successful

    def download_java_complete(self):
        self.accepted()

    def download_java_progress_change(self, progress):
        self.progress_bar.setValue(progress / 2)
        if (progress % 20) == 0:
            self.txtStdout.append('...')

    def save_configuration(self, configuration):
        settings = QSettings()
        settings.setValue(
            'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export',
            configuration.xtffile)
        settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log',
                          not self.log_config.isCollapsed())

    def restore_configuration(self):
        settings = QSettings()
        self.xtf_file_line_edit.setText(
            settings.value(
                'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export'))

        # Show log
        value_show_log = settings.value(
            'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool)
        self.log_config.setCollapsed(not value_show_log)

        # set model repository
        # if there is no option by default use online model repository
        custom_model_is_checked = settings.value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        if custom_model_is_checked:
            self.custom_model_directories = settings.value(
                'Asistente-LADM_COL/models/custom_models', DEFAULT_MODELS_DIR)

    def update_configuration(self):
        """
        Get the configuration that is updated with the user configuration changes on the dialog.
        :return: Configuration
        """
        db_factory = self._dbs_supported.get_db_factory(self.db.engine)

        configuration = ExportConfiguration()
        db_factory.set_ili2db_configuration_params(self.db.dict_conn_params,
                                                   configuration)

        configuration.xtffile = self.xtf_file_line_edit.text().strip()
        full_java_exe_path = JavaUtils.get_full_java_exe_path()
        if full_java_exe_path:
            self.base_configuration.java_path = full_java_exe_path

        # User could have changed the default values
        self.use_local_models = QSettings().value(
            'Asistente-LADM_COL/models/custom_model_directories_is_checked',
            DEFAULT_USE_CUSTOM_MODELS,
            type=bool)
        self.custom_model_directories = QSettings().value(
            'Asistente-LADM_COL/models/custom_models', DEFAULT_MODELS_DIR)

        # Check custom model directories
        if self.use_local_models:
            if not self.custom_model_directories:
                self.base_configuration.custom_model_directories_enabled = False
            else:
                self.base_configuration.custom_model_directories = self.custom_model_directories
                self.base_configuration.custom_model_directories_enabled = True

        configuration.base_configuration = self.base_configuration
        if self.get_ili_models():
            configuration.iliexportmodels = ';'.join(self.get_ili_models())
            configuration.ilimodels = ';'.join(self.get_ili_models())

        configuration.disable_validation = not QSettings().value(
            'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting',
            True, bool)

        return configuration

    def print_info(self, text, text_color='#000000', clear=False):
        self.txtStdout.setTextColor(QColor(text_color))
        self.txtStdout.append(text)
        QCoreApplication.processEvents()

    def on_stderr(self, text):
        color_log_text(text, self.txtStdout)
        self.advance_progress_bar_by_text(text)

    def on_process_started(self, command):
        self.disable()
        self.txtStdout.setTextColor(QColor('#000000'))
        self.txtStdout.clear()
        self.txtStdout.setText(command)
        QCoreApplication.processEvents()

    def on_process_finished(self, exit_code, result):
        color = '#004905' if exit_code == 0 else '#aa2222'
        self.txtStdout.setTextColor(QColor(color))
        self.txtStdout.append(
            QCoreApplication.translate("DialogExportData",
                                       "Finished ({})").format(exit_code))
        if result == iliexporter.Exporter.SUCCESS:
            self.buttonBox.clear()
            self.buttonBox.setEnabled(True)
            self.buttonBox.addButton(QDialogButtonBox.Close)
        else:
            self.enable()

            # Open log
            if self.log_config.isCollapsed():
                self.log_config.setCollapsed(False)

    def advance_progress_bar_by_text(self, text):
        if text.strip() == 'Info: compile models...':
            self.progress_bar.setValue(50)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: process data...':
            self.progress_bar.setValue(55)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: first validation pass...':
            self.progress_bar.setValue(70)
            QCoreApplication.processEvents()
        elif text.strip() == 'Info: second validation pass...':
            self.progress_bar.setValue(85)
            QCoreApplication.processEvents()

    def clear_messages(self):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.txtStdout.clear()  # Clear previous log messages
        self.progress_bar.setValue(0)  # Initialize progress bar

    def show_help(self):
        self.qgis_utils.show_help("export_data")

    def disable(self):
        self.db_config.setEnabled(False)
        self.ili_config.setEnabled(False)
        self.buttonBox.setEnabled(False)

    def enable(self):
        self.db_config.setEnabled(True)
        self.ili_config.setEnabled(True)
        self.buttonBox.setEnabled(True)

    def show_message(self, message, level):
        if level == Qgis.Warning:
            self.enable()

        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0)

    def xtf_browser_opened_to_true(self):
        """
        Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = True

    def xtf_browser_opened_to_false(self):
        """
        Slot. Sets a flag to false to eventually ask a user whether to overwrite a file.
        """
        self.xtf_browser_was_opened = False
        self.clear_messages()
Example #24
0
class FKProperty(WIDGET, BASE):
    """
    Editor to create/edit ForeignKey column property
    """
    def __init__(self, parent, relation={}):
        """
        :param parent: Owner of the form
        :type parent: QWidget
        :param relation: Dictionary holding fields used to build foreign key column
         *entity_relation - EntityRelation object, if its None then
         this is a new column else its an edit
         *fk_entities - entities used for ForeignKey selection
         *profile - current profile
         *entity - current entity you are creating column for.
         *column_name - name of the column
        :type form_field: dictionary
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self._entity_relation = relation['form_fields']['entity_relation']
        self.fk_entities = relation['fk_entities']
        self.profile = relation['profile']
        self.entity = relation['entity']
        self.column_name = relation['column_name']
        self.in_db = relation['form_fields']['in_db']
        self._show_in_parent = relation['show_in_parent']
        self._show_in_child = relation['show_in_child']
        self.column_model = QStandardItemModel()
        self.lvDisplayCol.setModel(self.column_model)

        self.init_gui()

    def init_gui(self):
        """
        Initializes form fields
        """
        self.cboPrimaryEntity.currentIndexChanged.connect( \
            self.load_entity_columns)

        self.load_fk_entities()
        if self._entity_relation:
            parent = self._entity_relation.parent.short_name
            parent_column = self._entity_relation.parent_column
            display_cols = self._entity_relation.display_cols

            self.cboPrimaryEntity.setCurrentIndex( \
                self.cboPrimaryEntity.findText(parent))

            self.cboPrimaryUKey.setCurrentIndex( \
                self.cboPrimaryUKey.findText(parent_column))

            self.show_display_cols(display_cols)

        # Disable controls if column exists in the database
        self.cboPrimaryEntity.setEnabled(not self.in_db)
        self.cboPrimaryUKey.setEnabled(not self.in_db)
        self.lvDisplayCol.setEnabled(not self.in_db)

        self.show_in_parent_chk.clicked.connect(self.on_show_in_parent_clicked)
        self.show_in_child_chk.clicked.connect(self.on_show_in_child_clicked)

    def on_show_in_parent_clicked(self):
        """
        A slot raised when show in parent is clicked.
        :return:
        :rtype:
        """
        if self.show_in_parent_chk.isChecked():
            self.show_in_child_chk.setChecked(False)
            self._show_in_parent = True

    def on_show_in_child_clicked(self):
        """
        A slot raised when show in child is clicked.
        :return:
        :rtype:
        """
        if self.show_in_child_chk.isChecked():
            self.show_in_parent_chk.setChecked(False)
            self._show_in_child = True

    def show_in_parent(self):
        """
        Returns show in parent.
        :return: Returns show in parent.
        :rtype: Boolean
        """
        return self._show_in_parent

    def show_in_child(self):
        """
        Returns show in child.
        :return: Returns show in child.
        :rtype: Boolean
        """
        return self._show_in_child

    def show_display_cols(self, display_cols):
        """
        checks previously selected display columns
        """
        for row in range(self.column_model.rowCount()):
            if str(self.column_model.item(row).text()) in display_cols:
                self.column_model.item(row).setCheckState(Qt.Checked)

    def load_fk_entities(self):
        """
        populates combobox with entities to select primary entity for the
        foreign key
        """
        self.cboPrimaryEntity.clear()
        self.cboPrimaryEntity.insertItems(
            0, [name[0] for name in self.fk_entities])

        self.cboPrimaryEntity.setCurrentIndex(0)

    def entity_columns(self):
        """
        returns: A list used to select child entity column when building
        a foreign key
        rtype: list
        """
        index = self.cboPrimaryEntity.currentIndex()

        entity_columns = \
            [column for column in self.fk_entities[index][1].columns.items()]

        column_names = [column[0] for column in entity_columns]

        return column_names

    def fk_display_columns(self):
        """
        returns: A list of columns used to select display columns
        in foreign key
        rtype: list
        """
        index = self.cboPrimaryEntity.currentIndex()
        entity_columns = \
            [column for column in self.fk_entities[index][1].columns.items()]

        columns = [column[0] for column in entity_columns \
                   if column[1].TYPE_INFO != 'SERIAL']

        return columns

    def load_entity_columns(self):
        """

        """
        columns = self.entity_columns()
        self.populate_column_combobox(columns)

        disp_columns = self.fk_display_columns()
        self.populate_column_listview(disp_columns)

    def populate_column_combobox(self, columns):
        """
        Populate combobox with column names
        param columns: List of entity columns to select your primary unique
        column for the foreign key
        type columns: list
        """
        self.cboPrimaryUKey.clear()
        self.cboPrimaryUKey.insertItems(0, columns)

    def populate_column_listview(self, columns):
        """
        Populates list view with columns used in selecting
        display columns for foreign key
        param columns: A list of column names
        type columns: list
        """
        self.column_model.clear()
        for column in columns:
            item = QStandardItem(column)
            item.setCheckable(True)
            self.column_model.appendRow(item)

    def add_values(self):
        """
        Construct an EntityRelation instance from form fields
        """
        er_fields = {}
        er_fields['parent'] = str(self.cboPrimaryEntity.currentText())
        er_fields['parent_column'] = str(self.cboPrimaryUKey.currentText())
        er_fields['display_columns'] = self.display_columns()
        er_fields['child'] = self.entity
        er_fields['child_column'] = self.column_name

        self._entity_relation = EntityRelation(self.profile, **er_fields)

    def display_columns(self):
        """
        Scans StandardItemModel for display columns, and returns a list of
        selected/checked columns for display in foreign key
        rtype: list
        """
        return [str(self.column_model.item(row).text()) \
                for row in range(self.column_model.rowCount()) \
                if self.column_model.item(row).checkState() == Qt.Checked]

    def entity_relation(self):
        """
        returns: entity relation instance
        rtype: EntityRelation
        """
        return self._entity_relation

    def accept(self):
        self.add_values()
        self.done(1)

    def reject(self):
        self.done(0)
class PosiviewProperties(QgsOptionsDialogBase, Ui_PosiviewPropertiesBase):
    '''
    GUI class classdocs for the Configuration dialog
    '''
    applyChanges = pyqtSignal(dict)

    def __init__(self, project, parent=None):
        '''
        Setup dialog widgets with the project properties
        '''
        super(PosiviewProperties, self).__init__("PosiViewProperties", parent)
        self.setupUi(self)
        self.groupBox_6.hide()
        self.initOptionsBase(False)
        self.restoreOptionsBaseUi()
        self.comboBoxParser.addItems(PARSERS)
        self.comboBoxProviderType.addItems(DEVICE_TYPES)
        self.project = project
        self.projectProperties = project.properties()
        self.mToolButtonLoad.setDefaultAction(self.actionLoadConfiguration)
        self.mToolButtonSave.setDefaultAction(self.actionSaveConfiguration)

        self.mobileModel = QStringListModel()
        self.mobileListModel = QStringListModel()
        self.mMobileListView.setModel(self.mobileListModel)
        self.mobileProviderModel = QStandardItemModel()
        self.mobileProviderModel.setHorizontalHeaderLabels(
            ('Provider', 'Filter'))
        self.mMobileProviderTableView.setModel(self.mobileProviderModel)

        self.providerListModel = QStringListModel()
        self.mDataProviderListView.setModel(self.providerListModel)
        self.comboBoxProviders.setModel(self.providerListModel)
        self.setupModelData(self.projectProperties)
        self.setupGeneralData(self.projectProperties)

    def setupModelData(self, properties):
        self.mobileListModel.setStringList(sorted(
            properties['Mobiles'].keys()))
        self.providerListModel.setStringList(
            sorted(properties['Provider'].keys()))

    def setupGeneralData(self, properties):
        self.lineEditCruise.setText(properties['Mission']['cruise'])
        self.lineEditDive.setText(properties['Mission']['dive'])
        self.lineEditStation.setText(properties['Mission']['station'])
        self.lineEditRecorderPath.setText(properties['RecorderPath'])
        self.checkBoxAutoRecording.setChecked(properties['AutoRecord'])
        self.spinBoxNotifyDuration.setValue(properties['NotifyDuration'])
        self.checkBoxUtcClock.setChecked(properties['ShowUtcClock'])
        self.checkBoxWithSuffix.setChecked(properties['DefaultFormat'] & 4)
        self.comboBoxDefaultPositionFormat.setCurrentIndex(
            (properties['DefaultFormat']) & 3)

    def updateGeneralData(self):
        self.projectProperties['Mission']['cruise'] = self.lineEditCruise.text(
        )
        self.projectProperties['Mission']['dive'] = self.lineEditDive.text()
        self.projectProperties['Mission'][
            'station'] = self.lineEditStation.text()
        self.projectProperties[
            'RecorderPath'] = self.lineEditRecorderPath.text()
        self.projectProperties[
            'AutoRecord'] = self.checkBoxAutoRecording.isChecked()
        self.projectProperties[
            'NotifyDuration'] = self.spinBoxNotifyDuration.value()
        self.projectProperties[
            'ShowUtcClock'] = self.checkBoxUtcClock.isChecked()
        self.projectProperties[
            'DefaultFormat'] = self.comboBoxDefaultPositionFormat.currentIndex(
            )
        if self.checkBoxWithSuffix.isChecked():
            self.projectProperties['DefaultFormat'] |= 4

    def getColor(self, value):
        try:
            return QColor.fromRgba(int(value))
        except ValueError:
            return QColor(value)

    @pyqtSlot(QAbstractButton, name='on_buttonBox_clicked')
    def onButtonBoxClicked(self, button):
        role = self.buttonBox.buttonRole(button)
        if role == QDialogButtonBox.ApplyRole or role == QDialogButtonBox.AcceptRole:
            self.updateGeneralData()
            self.applyChanges.emit(self.projectProperties)

    @pyqtSlot(name='on_actionSaveConfiguration_triggered')
    def onActionSaveConfigurationTriggered(self):
        ''' Save the current configuration
        '''
        fn, __ = QFileDialog.getSaveFileName(None,
                                             'Save PosiView configuration', '',
                                             'Configuration (*.ini *.conf)')
        if fn:
            if not os.path.splitext(fn)[1]:
                fn += u'.conf'
            self.project.store(fn)

    @pyqtSlot(name='on_actionLoadConfiguration_triggered')
    def onActionLoadConfigurationTriggered(self):
        ''' Load configuration from file
        '''
        fn, __ = QFileDialog.getOpenFileName(None,
                                             'Save PosiView configuration', '',
                                             'Configuration (*.ini *.conf)')
        self.projectProperties = self.project.read(fn)
        self.setupModelData(self.projectProperties)
        self.setupGeneralData(self.projectProperties)

    @pyqtSlot(QModelIndex, name='on_mMobileListView_clicked')
    def editMobile(self, index):
        ''' Populate the widgets with the selected mobiles properties
        '''
        if index.isValid():
            self.populateMobileWidgets(index)

    @pyqtSlot(str, name='on_comboBoxMobileType_currentIndexChanged')
    def mobileTypeChanged(self, mType):
        if mType == 'SHAPE':
            self.lineEditMobileShape.setEnabled(True)
        else:
            self.lineEditMobileShape.setEnabled(False)

    @pyqtSlot(QModelIndex, name='on_mMobileListView_activated')
    def activated(self, index):
        pass

    @pyqtSlot(name='on_toolButtonAddMobile_clicked')
    def addMobile(self):
        self.mobileListModel.insertRow(self.mobileListModel.rowCount())
        index = self.mobileListModel.index(self.mobileListModel.rowCount() - 1)
        self.lineEditMobileName.setText('NewMobile')
        self.mobileListModel.setData(index, 'NewMobile', Qt.DisplayRole)
        self.mMobileListView.setCurrentIndex(index)
        self.applyMobile()

    @pyqtSlot(name='on_pushButtonApplyMobile_clicked')
    def applyMobile(self):
        index = self.mMobileListView.currentIndex()
        if index.isValid() and not self.lineEditMobileName.text() == '':
            mobile = dict()
            mobile['Name'] = self.lineEditMobileName.text()
            mobile['type'] = self.comboBoxMobileType.currentText()
            try:
                t = eval(self.lineEditMobileShape.text())
                if t.__class__ is tuple or t.__class__ is dict:
                    mobile['shape'] = t
            except SyntaxError:
                mobile['shape'] = ((0.0, -0.5), (0.3, 0.5), (0.0, 0.2), (-0.5,
                                                                         0.5))
            mobile['length'] = self.doubleSpinBoxMobileLength.value()
            mobile['width'] = self.doubleSpinBoxMobileWidth.value()
            mobile['defaultIcon'] = self.checkBoxDefaultIcon.isChecked()
            mobile['defaultIconFilled'] = self.checkBoxDefIconFilled.isChecked(
            )
            mobile['offsetX'] = self.doubleSpinBoxXOffset.value()
            mobile['offsetY'] = self.doubleSpinBoxYOffset.value()
            mobile['zValue'] = self.spinBoxZValue.value()
            mobile['color'] = self.mColorButtonMobileColor.color().rgba()
            mobile['fillColor'] = self.mColorButtonMobileFillColor.color(
            ).rgba()
            mobile['timeout'] = self.spinBoxMobileTimeout.value() * 1000
            mobile['nofixNotify'] = self.spinBoxMobileNotification.value()
            mobile['fadeOut'] = self.checkBoxFadeOut.isChecked()
            mobile['trackLength'] = self.spinBoxTrackLength.value()
            mobile['trackColor'] = self.mColorButtonMobileTrackColor.color(
            ).rgba()
            mobile['showLabel'] = self.checkBoxShowLabel.isChecked()
            provs = dict()
            for r in range(self.mobileProviderModel.rowCount()):
                try:
                    fil = int(
                        self.mobileProviderModel.item(r,
                                                      1).data(Qt.DisplayRole))
                except Exception:
                    fil = self.mobileProviderModel.item(r,
                                                        1).data(Qt.DisplayRole)
                    if not fil:
                        fil = None
                provs[self.mobileProviderModel.item(r, 0).data(
                    Qt.DisplayRole)] = fil
            mobile['provider'] = provs
            currName = self.mobileListModel.data(index, Qt.DisplayRole)
            if not currName == mobile['Name']:
                del self.projectProperties['Mobiles'][currName]
                self.mobileListModel.setData(index, mobile['Name'],
                                             Qt.DisplayRole)
            self.projectProperties['Mobiles'][mobile['Name']] = mobile

    def populateMobileWidgets(self, index):
        mobile = self.projectProperties['Mobiles'][self.mobileListModel.data(
            index, Qt.DisplayRole)]
        self.lineEditMobileName.setText(mobile.get('Name'))
        self.comboBoxMobileType.setCurrentIndex(
            self.comboBoxMobileType.findText(
                mobile.setdefault('type', 'BOX').upper()))
        if mobile['type'] == 'SHAPE':
            self.lineEditMobileShape.setText(str(mobile['shape']))
            self.lineEditMobileShape.setEnabled(True)
            self.doubleSpinBoxXOffset.setEnabled(True)
            self.doubleSpinBoxYOffset.setEnabled(True)
        else:
            self.lineEditMobileShape.setEnabled(False)
            self.doubleSpinBoxXOffset.setEnabled(False)
            self.doubleSpinBoxYOffset.setEnabled(False)
            self.lineEditMobileShape.clear()
        self.doubleSpinBoxMobileLength.setValue(mobile.get('length', 20.0))
        self.doubleSpinBoxMobileWidth.setValue(mobile.get('width', 5.0))
        self.checkBoxDefaultIcon.setChecked(mobile.get('defaultIcon', True))
        self.checkBoxDefIconFilled.setChecked(
            mobile.get('defaultIconFilled', False))
        self.doubleSpinBoxXOffset.setValue(mobile.get('offsetX', 0.0))
        self.doubleSpinBoxYOffset.setValue(mobile.get('offsetY', 0.0))
        self.spinBoxZValue.setValue(mobile.get('zValue', 100))
        self.mColorButtonMobileColor.setColor(
            self.getColor(mobile.get('color', 'black')))
        self.mColorButtonMobileFillColor.setColor(
            self.getColor(mobile.get('fillColor', 'green')))
        self.spinBoxMobileTimeout.setValue(mobile.get('timeout', 3000) / 1000)
        self.spinBoxMobileNotification.setValue(mobile.get('nofixNotify', 0))
        self.checkBoxFadeOut.setChecked(mobile.get('fadeOut', False))
        self.spinBoxTrackLength.setValue(mobile.get('trackLength', 100))
        self.mColorButtonMobileTrackColor.setColor(
            self.getColor(mobile.get('trackColor', 'green')))
        self.checkBoxShowLabel.setChecked(mobile.get('showLabel', False))
        r = 0
        self.mobileProviderModel.removeRows(
            0, self.mobileProviderModel.rowCount())
        if 'provider' in mobile:
            for k, v in list(mobile['provider'].items()):
                prov = QStandardItem(k)
                val = QStandardItem(str(v))
                self.mobileProviderModel.setItem(r, 0, prov)
                self.mobileProviderModel.setItem(r, 1, val)
                r += 1

    @pyqtSlot(name='on_toolButtonRemoveMobile_clicked')
    def removeMobile(self):
        idx = self.mMobileListView.currentIndex()
        if idx.isValid():
            self.projectProperties['Mobiles'].pop(
                self.mobileListModel.data(idx, Qt.DisplayRole))
            self.mobileListModel.removeRows(idx.row(), 1)
            idx = self.mMobileListView.currentIndex()
            if idx.isValid():
                self.populateMobileWidgets(idx)

    @pyqtSlot(name='on_toolButtonRefreshMobileProvider_clicked')
    def refreshMobileProvider(self):
        prov = self.comboBoxProviders.currentText()
        if prov == '':
            return
        fil = None
        if self.lineEditProviderFilter.text() != '':
            fil = self.lineEditProviderFilter.text()
        items = self.mobileProviderModel.findItems(prov, Qt.MatchExactly, 0)
        if items:
            for item in items:
                self.mobileProviderModel.setItem(item.row(), 1,
                                                 QStandardItem(fil))
        else:
            self.mobileProviderModel.appendRow(
                [QStandardItem(prov), QStandardItem(fil)])

    @pyqtSlot(name='on_toolButtonRemoveMobileProvider_clicked')
    def removeMobileProvider(self):
        idx = self.mMobileProviderTableView.currentIndex()
        if idx.isValid():
            self.mobileProviderModel.removeRow(idx.row())

    @pyqtSlot(name='on_pushButtonApplyDataProvider_clicked')
    def applyDataProvider(self):
        index = self.mDataProviderListView.currentIndex()
        if index.isValid() and not self.lineEditProviderName.text() == '':
            provider = dict()
            provider['Name'] = self.lineEditProviderName.text()
            provider['DataDeviceType'] = self.comboBoxProviderType.currentText(
            )
            if provider['DataDeviceType'] in NETWORK_TYPES:
                provider['Host'] = self.lineEditProviderHostName.text()
                provider['Port'] = self.spinBoxProviderPort.value()
            provider['Parser'] = self.comboBoxParser.currentText()
            currName = self.providerListModel.data(index, Qt.DisplayRole)
            if not currName == provider['Name']:
                del self.projectProperties['Provider'][currName]
                self.providerListModel.setData(index, provider['Name'],
                                               Qt.DisplayRole)
            self.projectProperties['Provider'][provider['Name']] = provider

    @pyqtSlot(QModelIndex, name='on_mDataProviderListView_clicked')
    def editDataProvider(self, index):
        '''
        '''
        if index.isValid():
            self.populateDataProviderWidgets(index)

    def populateDataProviderWidgets(self, index):
        provider = self.projectProperties['Provider'][
            self.providerListModel.data(index, Qt.DisplayRole)]
        self.lineEditProviderName.setText(provider.get('Name'))
        self.comboBoxProviderType.setCurrentIndex(
            self.comboBoxProviderType.findText(
                provider.setdefault('DataDeviceType', 'UDP').upper()))
        if provider['DataDeviceType'] in NETWORK_TYPES:
            self.stackedWidgetDataDevice.setCurrentIndex(0)
            self.lineEditProviderHostName.setText(
                provider.setdefault('Host', '0.0.0.0'))
            self.spinBoxProviderPort.setValue(
                int(provider.setdefault('Port', 2000)))

        self.comboBoxParser.setCurrentIndex(
            self.comboBoxParser.findText(
                provider.setdefault('Parser', 'NONE').upper()))

    @pyqtSlot(name='on_toolButtonAddDataProvider_clicked')
    def addDataProvider(self):
        self.providerListModel.insertRow(self.providerListModel.rowCount())
        index = self.providerListModel.index(
            self.providerListModel.rowCount() - 1)
        self.lineEditProviderName.setText('NewDataProvider')
        self.providerListModel.setData(index, 'NewDataProvider',
                                       Qt.DisplayRole)
        self.mDataProviderListView.setCurrentIndex(index)
        self.applyDataProvider()

    @pyqtSlot(name='on_toolButtonRemoveDataProvider_clicked')
    def removeDataProvider(self):
        idx = self.mDataProviderListView.currentIndex()
        if idx.isValid():
            self.projectProperties['Provider'].pop(
                self.providerListModel.data(idx, Qt.DisplayRole))
            self.providerListModel.removeRows(idx.row(), 1)
            idx = self.mDataProviderListView.currentIndex()
            if idx.isValid():
                self.populateDataProviderWidgets(idx)

    @pyqtSlot(name='on_toolButtonSelectLogPath_clicked')
    def selectRecorderPath(self):
        path = QFileDialog.getExistingDirectory(
            self, self.tr('Select Recorder Path'),
            self.lineEditRecorderPath.text(),
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        if path != '':
            self.lineEditRecorderPath.setText(path)

    @pyqtSlot(QPoint, name='on_lineEditMobileShape_customContextMenuRequested')
    def mobileShapeContextMenu(self, pos):
        menu = QMenu(self.lineEditMobileShape)
        vesselAction = menu.addAction(self.tr('Vessel'))
        rovAction = menu.addAction(self.tr('ROV'))
        auvAction = menu.addAction(self.tr('AUV'))
        arrowAction = menu.addAction(self.tr('Arrow'))
        selectedAction = menu.exec_(self.lineEditMobileShape.mapToGlobal(pos))
        if selectedAction == vesselAction:
            self.lineEditMobileShape.setText(
                u'((0, -0.5), (0.5, -0.3), (0.5, 0.5), (-0.5, 0.5), (-0.5, -0.3))'
            )
        elif selectedAction == rovAction:
            self.lineEditMobileShape.setText(
                u'((0.3, -0.5), (0.5, -0.3), (0.5, 0.5), (-0.5, 0.5), (-0.5, -0.3), (-0.3, -0.5))'
            )
        elif selectedAction == auvAction:
            self.lineEditMobileShape.setText(
                u'((0, -0.5), (0.4, -0.3), (0.5, -0.3), (0.5, -0.2), (0.4, -0.2), (0.4, 0.3), (0.5, 0.3), (0.5, 0.4), (0.4, 0.4), (0.0, 0.5), \
             (-0.4, 0.4), (-0.5, 0.4), (-0.5, 0.3), (-0.4, 0.3), (-0.4, -0.2), (-0.5, -0.2), (-0.5, -0.3), (-0.4, -0.3))'
            )
        elif selectedAction == arrowAction:
            self.lineEditMobileShape.setText(
                u'((0, -0.5), (0.5, 0.5), (0, 0), (-0.5, 0.5))')

    @pyqtSlot(name='on_buttonBox_helpRequested')
    def showHelp(self):
        """Display application help to the user."""
        help_file = os.path.join(
            os.path.split(os.path.dirname(__file__))[0], 'help', 'index.html')
        QDesktopServices.openUrl(QUrl.fromLocalFile(help_file))
Example #26
0
class ModelAtrributesView(QListView):
    """
    Custom QListView implementation that displays checkable model attributes.
    """
    def __init__(self, parent=None, dataModel=None):
        QListView.__init__(self, parent)

        self._dataModel = dataModel
        self._selectedDisplayMapping = OrderedDict()
        self._modelDisplayMapping = OrderedDict()
        self._attrModel = QStandardItemModel(self)

    def dataModel(self):
        """
        Returns the data model instance.
        """
        return self._dataModel

    def setDataModel(self, dataModel):
        """
        Sets the data model. Should be a callable class rather than the class.
        instance.
        """
        if callable(dataModel):
            self._dataModel = dataModel

        else:
            self._dataModel = dataModel.__class__

    def modelDisplayMapping(self):
        """
        Returns the column name and display name collection.
        """
        return self._modelDisplayMapping

    def setModelDisplayMapping(self, dataMapping):
        """
        Sets the mapping dictionary for the table object
        """
        if dataMapping != None:
            self._modelDisplayMapping = dataMapping

    def load(self, sort=False):
        """
        Load the model's attributes into the list view.
        """
        if self._dataModel == None:
            return

        try:
            self._loadAttrs(self._dataModel.displayMapping(), sort)
        except AttributeError:
            # Ignore error if model does not contain
            # the displayMapping static method
            pass

    def load_mapping(self, mapping, sort=False):
        """
        Load collection containing column name and corresponding display name.
        """
        self._modelDisplayMapping = mapping

        self._loadAttrs(mapping, sort)

    def sort(self):
        """
        Sorts display name in ascending order.
        """
        self._attrModel.sort(0)

    def _loadAttrs(self, attrMapping, sort=False):
        """
        Loads display mapping into the list view.
        Specify to sort display names in ascending order once items have been
        added to the model.
        """
        self._attrModel.clear()
        self._attrModel.setColumnCount(2)

        for attrName, displayName in attrMapping.items():
            # Exclude row ID in the list, other unique identifier attributes in the model can be used
            if attrName != "id":
                displayNameItem = QStandardItem(displayName)
                displayNameItem.setCheckable(True)
                attrNameItem = QStandardItem(attrName)

                self._attrModel.appendRow([displayNameItem, attrNameItem])

        self.setModel(self._attrModel)

        if sort:
            self._attrModel.sort(0)

    def selectedMappings(self):
        """
        Return a dictionary of field names and their corresponding display values.
        """
        selectedAttrs = OrderedDict()

        for i in range(self._attrModel.rowCount()):
            displayNameItem = self._attrModel.item(i, 0)

            if displayNameItem.checkState() == Qt.Checked:
                attrNameItem = self._attrModel.item(i, 1)

                selectedAttrs[attrNameItem.text()] = displayNameItem.text()

        return selectedAttrs
Example #27
0
class AbstractSTREnityListView(QListView):
    """
    A widget for listing and selecting one or more STR entities.
    .. versionadded:: 1.7
    """

    def __init__(self, parent=None, **kwargs):
        super(AbstractSTREnityListView, self).__init__(parent)

        self._model = QStandardItemModel(self)
        self._model.setColumnCount(1)
        self.setModel(self._model)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self._model.itemChanged.connect(self._on_item_changed)

        self._profile = kwargs.get('profile', None)
        self._social_tenure = kwargs.get('social_tenure', None)

        # Load appropriate entities to the view
        if not self._profile is None:
            self._load_profile_entities()

        # Load entities in the STR definition
        if not self._social_tenure is None:
            self._select_str_entities()

    def _on_item_changed(self, item):
        # Emit signals when an item has been (de)selected. To be
        # implemented by subclasses.
        pass

    @property
    def profile(self):
        """
        :return: Returns the current profile object in the configuration.
        :rtype: Profile
        """
        return self._profile

    @profile.setter
    def profile(self, profile):
        """
        Sets the current profile object in the configuration.
        :param profile: Profile object.
        :type profile: Profile
        """
        self._profile = profile
        self._load_profile_entities()

    @property
    def social_tenure(self):
        """
        :return: Returns the profile's social tenure entity.
        :rtype: SocialTenure
        """
        return self._social_tenure

    @social_tenure.setter
    def social_tenure(self, social_tenure):
        """
        Set the social_tenure entity.
        :param social_tenure: A profile's social tenure entity.
        :type social_tenure: SocialTenure
        """
        self._social_tenure = social_tenure
        self._select_str_entities()

    def _select_str_entities(self):
        """
        Select the entities defined in the STR. E.g. parties for party
        entity and spatial units for spatial unit entity. Default
        implementation does nothing, to be implemented by subclasses.
        """
        pass

    def _load_profile_entities(self):
        # Reset view
        self.clear()

        # Populate entity items in the view
        for e in self._profile.user_entities():
            self._add_entity(e)

    def _add_entity(self, entity):
        # Add entity item to view
        item = QStandardItem(
            GuiUtils.get_icon('table.png'),
            entity.short_name
        )
        item.setCheckable(True)
        item.setCheckState(Qt.Unchecked)

        self._model.appendRow(item)

    def select_entities(self, entities):
        """
        Checks STR entities in the view and emit the entity_selected
        signal for each item selected.
        :param entities: Collection of STR entities.
        :type entities: list
        """
        # Clear selection
        self.clear_selection()

        for e in entities:
            name = e.short_name
            self.select_entity(name)

    def selected_entities(self):
        """
        :return: Returns a list of selected entity short names.
        :rtype: list
        """
        selected_items = []

        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                selected_items.append(item.text())

        return selected_items

    def clear(self):
        """
        Remove all party items in the view.
        """
        self._model.clear()
        self._model.setColumnCount(1)

    def clear_selection(self):
        """
        Uncheck all items in the view.
        """
        for i in range(self._model.rowCount()):
            item = self._model.item(i)
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)

    def select_entity(self, name):
        """
        Selects a party entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Unchecked:
                item.setCheckState(Qt.Checked)

    def deselect_entity(self, name):
        """
        Deselects an entity with the given short name.
        :param name: Entity short name
        :type name: str
        """
        items = self._model.findItems(name)
        if len(items) > 0:
            item = items[0]
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)
Example #28
0
class MultipleInputDialog(BASE, WIDGET):
    def __init__(self, options, selectedoptions=None, datatype=None):
        super(MultipleInputDialog, self).__init__(None)
        self.setupUi(self)
        self.datatype = datatype
        self.model = None

        self.options = []
        for i, option in enumerate(options):
            if option is None or isinstance(option, str):
                self.options.append((i, option))
            else:
                self.options.append((option[0], option[1]))

        self.selectedoptions = selectedoptions or []

        # Additional buttons
        self.btnSelectAll = QPushButton(self.tr('Select All'))
        self.buttonBox.addButton(self.btnSelectAll,
                                 QDialogButtonBox.ActionRole)
        self.btnClearSelection = QPushButton(self.tr('Clear Selection'))
        self.buttonBox.addButton(self.btnClearSelection,
                                 QDialogButtonBox.ActionRole)
        self.btnToggleSelection = QPushButton(self.tr('Toggle Selection'))
        self.buttonBox.addButton(self.btnToggleSelection,
                                 QDialogButtonBox.ActionRole)
        if self.datatype is not None:
            btnAddFile = QPushButton(
                QCoreApplication.translate("MultipleInputDialog",
                                           'Add File(s)…'))
            btnAddFile.clicked.connect(self.addFiles)
            self.buttonBox.addButton(btnAddFile, QDialogButtonBox.ActionRole)

        self.btnSelectAll.clicked.connect(lambda: self.selectAll(True))
        self.btnClearSelection.clicked.connect(lambda: self.selectAll(False))
        self.btnToggleSelection.clicked.connect(self.toggleSelection)

        self.settings = QgsSettings()
        self.restoreGeometry(
            self.settings.value("/Processing/multipleInputDialogGeometry",
                                QByteArray()))

        self.lstLayers.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.lstLayers.setDragDropMode(QAbstractItemView.InternalMove)

        self.populateList()
        self.finished.connect(self.saveWindowGeometry)

    def saveWindowGeometry(self):
        self.settings.setValue("/Processing/multipleInputDialogGeometry",
                               self.saveGeometry())

    def populateList(self):
        self.model = QStandardItemModel()
        for value, text in self.options:
            item = QStandardItem(text)
            item.setData(value, Qt.UserRole)
            item.setCheckState(Qt.Checked if value in
                               self.selectedoptions else Qt.Unchecked)
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        # add extra options (e.g. manually added layers)
        for t in [o for o in self.selectedoptions if not isinstance(o, int)]:
            if isinstance(t, QgsProcessingModelChildParameterSource):
                item = QStandardItem(t.staticValue())
            else:
                item = QStandardItem(t)
            item.setData(item.text(), Qt.UserRole)
            item.setCheckState(Qt.Checked)
            item.setCheckable(True)
            item.setDropEnabled(False)
            self.model.appendRow(item)

        self.lstLayers.setModel(self.model)

    def accept(self):
        self.selectedoptions = []
        model = self.lstLayers.model()
        for i in range(model.rowCount()):
            item = model.item(i)
            if item.checkState() == Qt.Checked:
                self.selectedoptions.append(item.data(Qt.UserRole))
        QDialog.accept(self)

    def reject(self):
        self.selectedoptions = None
        QDialog.reject(self)

    def getItemsToModify(self):
        items = []
        if len(self.lstLayers.selectedIndexes()) > 1:
            for i in self.lstLayers.selectedIndexes():
                items.append(self.model.itemFromIndex(i))
        else:
            for i in range(self.model.rowCount()):
                items.append(self.model.item(i))
        return items

    def selectAll(self, value):
        for item in self.getItemsToModify():
            item.setCheckState(Qt.Checked if value else Qt.Unchecked)

    def toggleSelection(self):
        for item in self.getItemsToModify():
            checked = item.checkState() == Qt.Checked
            item.setCheckState(Qt.Unchecked if checked else Qt.Checked)

    def getFileFilter(self, datatype):
        """
        Returns a suitable file filter pattern for the specified parameter definition
        :param param:
        :return:
        """
        if datatype == QgsProcessing.TypeRaster:
            return QgsProviderRegistry.instance().fileRasterFilters()
        elif datatype == QgsProcessing.TypeFile:
            return self.tr('All files (*.*)')
        else:
            exts = QgsVectorFileWriter.supportedFormatExtensions()
            for i in range(len(exts)):
                exts[i] = self.tr('{0} files (*.{1})').format(
                    exts[i].upper(), exts[i].lower())
            return self.tr('All files (*.*)') + ';;' + ';;'.join(exts)

    def addFiles(self):
        filter = self.getFileFilter(self.datatype)

        settings = QgsSettings()
        path = str(settings.value('/Processing/LastInputPath'))

        ret, selected_filter = QFileDialog.getOpenFileNames(
            self, self.tr('Select File(s)'), path, filter)
        if ret:
            files = list(ret)
            settings.setValue('/Processing/LastInputPath',
                              os.path.dirname(str(files[0])))
            for filename in files:
                item = QStandardItem(filename)
                item.setData(filename, Qt.UserRole)
                item.setCheckState(Qt.Checked)
                item.setCheckable(True)
                item.setDropEnabled(False)
                self.model.appendRow(item)
class ModelSelectionDialog(uicls, basecls):
    """Dialog for model selection."""

    TABLE_LIMIT = 10
    NAME_COLUMN_IDX = 1

    def __init__(self, plugin_dock, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.plugin_dock = plugin_dock
        self.communication = self.plugin_dock.communication
        self.current_user = self.plugin_dock.current_user
        self.threedi_api = self.plugin_dock.threedi_api
        self.organisations = self.plugin_dock.organisations
        self.threedi_models = None
        self.simulation_templates = None
        self.current_model = None
        self.current_model_cells = None
        self.current_model_breaches = None
        self.current_simulation_template = None
        self.cells_layer = None
        self.breaches_layer = None
        self.organisation = None
        self.model_is_loaded = False
        self.models_model = QStandardItemModel()
        self.models_tv.setModel(self.models_model)
        self.templates_model = QStandardItemModel()
        self.templates_tv.setModel(self.templates_model)
        self.pb_prev_page.clicked.connect(self.move_models_backward)
        self.pb_next_page.clicked.connect(self.move_models_forward)
        self.page_sbox.valueChanged.connect(self.fetch_3di_models)
        self.pb_load.clicked.connect(self.load_model)
        self.pb_cancel_load.clicked.connect(self.cancel_load_model)
        self.search_le.returnPressed.connect(self.search_model)
        self.models_tv.selectionModel().selectionChanged.connect(
            self.refresh_templates_list)
        self.templates_tv.selectionModel().selectionChanged.connect(
            self.toggle_load_model)
        self.populate_organisations()
        self.fetch_3di_models()

    def refresh_templates_list(self):
        """Refresh simulation templates list if any model is selected."""
        selection_model = self.models_tv.selectionModel()
        self.templates_model.clear()
        self.templates_page_sbox.setMaximum(1)
        self.templates_page_sbox.setSuffix(" / 1")
        if selection_model.hasSelection():
            self.fetch_simulation_templates()
            if self.templates_model.rowCount() > 0:
                row_idx = self.templates_model.index(0, 0)
                self.templates_tv.selectionModel().setCurrentIndex(
                    row_idx, QItemSelectionModel.ClearAndSelect)
        self.toggle_load_model()

    def toggle_load_model(self):
        """Toggle load button if any model is selected."""
        selection_model = self.templates_tv.selectionModel()
        if selection_model.hasSelection():
            self.pb_load.setEnabled(True)
        else:
            self.pb_load.setDisabled(True)

    def move_models_backward(self):
        """Moving to the models previous results page."""
        self.page_sbox.setValue(self.page_sbox.value() - 1)

    def move_models_forward(self):
        """Moving to the models next results page."""
        self.page_sbox.setValue(self.page_sbox.value() + 1)

    def move_templates_backward(self):
        """Moving to the templates previous results page."""
        self.templates_page_sbox.setValue(self.page_sbox.value() - 1)

    def move_templates_forward(self):
        """Moving to the templates next results page."""
        self.templates_page_sbox.setValue(self.page_sbox.value() + 1)

    def populate_organisations(self):
        """Populating organisations list inside combo box."""
        for org in self.organisations.values():
            self.organisations_box.addItem(org.name, org)

    def fetch_3di_models(self):
        """Fetching 3Di models list."""
        try:
            tc = ThreediCalls(self.threedi_api)
            offset = (self.page_sbox.value() - 1) * self.TABLE_LIMIT
            text = self.search_le.text()
            threedi_models, models_count = tc.fetch_3di_models_with_count(
                limit=self.TABLE_LIMIT, offset=offset, name_contains=text)
            pages_nr = ceil(models_count / self.TABLE_LIMIT) or 1
            self.page_sbox.setMaximum(pages_nr)
            self.page_sbox.setSuffix(f" / {pages_nr}")
            self.models_model.clear()
            header = [
                "ID", "Model", "Schematisation", "Revision", "Last updated",
                "Updated by"
            ]
            self.models_model.setHorizontalHeaderLabels(header)
            for sim_model in sorted(threedi_models,
                                    key=attrgetter("revision_commit_date"),
                                    reverse=True):
                id_item = QStandardItem(str(sim_model.id))
                name_item = QStandardItem(sim_model.name)
                name_item.setData(sim_model, role=Qt.UserRole)
                schema_item = QStandardItem(sim_model.schematisation_name)
                rev_item = QStandardItem(sim_model.revision_number)
                last_updated_day = sim_model.revision_commit_date.split("T")[0]
                lu_datetime = QDateTime.fromString(last_updated_day,
                                                   "yyyy-MM-dd")
                lu_item = QStandardItem(lu_datetime.toString("dd-MMMM-yyyy"))
                ub_item = QStandardItem(sim_model.user)
                self.models_model.appendRow([
                    id_item, name_item, schema_item, rev_item, lu_item, ub_item
                ])
            self.threedi_models = threedi_models
        except ApiException as e:
            self.close()
            error_msg = extract_error_message(e)
            self.communication.show_error(error_msg)
        except Exception as e:
            self.close()
            error_msg = f"Error: {e}"
            self.communication.show_error(error_msg)

    def fetch_simulation_templates(self):
        """Fetching simulation templates list."""
        try:
            tc = ThreediCalls(self.threedi_api)
            offset = (self.templates_page_sbox.value() - 1) * self.TABLE_LIMIT
            selected_model = self.get_selected_model()
            model_pk = selected_model.id
            templates, templates_count = tc.fetch_simulation_templates_with_count(
                model_pk, limit=self.TABLE_LIMIT, offset=offset)
            pages_nr = ceil(templates_count / self.TABLE_LIMIT) or 1
            self.templates_page_sbox.setMaximum(pages_nr)
            self.templates_page_sbox.setSuffix(f" / {pages_nr}")
            self.templates_model.clear()
            header = ["Template ID", "Template name", "Creation date"]
            self.templates_model.setHorizontalHeaderLabels(header)
            for template in sorted(templates,
                                   key=attrgetter("id"),
                                   reverse=True):
                id_item = QStandardItem(str(template.id))
                name_item = QStandardItem(template.name)
                name_item.setData(template, role=Qt.UserRole)
                creation_date = template.created.strftime(
                    "%d-%m-%Y") if template.created else ""
                creation_date_item = QStandardItem(creation_date)
                self.templates_model.appendRow(
                    [id_item, name_item, creation_date_item])
            for i in range(len(header)):
                self.templates_tv.resizeColumnToContents(i)
            self.simulation_templates = templates
        except ApiException as e:
            error_msg = extract_error_message(e)
            self.communication.show_error(error_msg)
        except Exception as e:
            error_msg = f"Error: {e}"
            self.communication.show_error(error_msg)

    def search_model(self):
        """Method used for searching models with text typed withing search bar."""
        self.page_sbox.valueChanged.disconnect(self.fetch_3di_models)
        self.page_sbox.setValue(1)
        self.page_sbox.valueChanged.connect(self.fetch_3di_models)
        self.fetch_3di_models()

    def load_cached_layers(self):
        """Loading cached layers into the map canvas."""
        if self.current_model_cells is not None:
            self.cells_layer = QgsVectorLayer(self.current_model_cells,
                                              "cells", "ogr")
            set_named_style(self.cells_layer, "cells.qml")
            QgsProject.instance().addMapLayer(self.cells_layer, False)
            QgsProject.instance().layerTreeRoot().insertLayer(
                0, self.cells_layer)
            self.cells_layer.setFlags(QgsMapLayer.Searchable
                                      | QgsMapLayer.Identifiable)
        if self.current_model_breaches is not None:
            self.breaches_layer = QgsVectorLayer(self.current_model_breaches,
                                                 "breaches", "ogr")
            set_named_style(self.breaches_layer, "breaches.qml")
            QgsProject.instance().addMapLayer(self.breaches_layer, False)
            QgsProject.instance().layerTreeRoot().insertLayer(
                0, self.breaches_layer)
            self.breaches_layer.setFlags(QgsMapLayer.Searchable
                                         | QgsMapLayer.Identifiable)
        if self.current_model_cells is not None:
            self.plugin_dock.iface.setActiveLayer(self.cells_layer)
            self.plugin_dock.iface.zoomToActiveLayer()

    def unload_cached_layers(self):
        """Removing model related vector layers from map canvas."""
        try:
            if self.breaches_layer is not None:
                QgsProject.instance().removeMapLayer(self.breaches_layer)
                self.breaches_layer = None
            if self.cells_layer is not None:
                QgsProject.instance().removeMapLayer(self.cells_layer)
                self.cells_layer = None
            self.plugin_dock.iface.mapCanvas().refresh()
        except AttributeError:
            pass

    def load_model(self):
        """Loading selected model."""
        index = self.models_tv.currentIndex()
        if index.isValid():
            self.organisation = self.organisations_box.currentData()
            self.unload_cached_layers()
            current_row = index.row()
            name_item = self.models_model.item(current_row,
                                               self.NAME_COLUMN_IDX)
            self.current_model = name_item.data(Qt.UserRole)
            self.current_model_cells = self.get_cached_data("cells")
            self.current_model_breaches = self.get_cached_data("breaches")
            self.current_simulation_template = self.get_selected_template()
            self.load_cached_layers()
            self.model_is_loaded = True
        self.close()

    def cancel_load_model(self):
        """Cancel loading model."""
        self.current_simulation_template = None
        self.model_is_loaded = False
        self.close()

    def get_selected_model(self):
        """Get currently selected model."""
        index = self.models_tv.currentIndex()
        if index.isValid():
            current_row = index.row()
            name_item = self.models_model.item(current_row,
                                               self.NAME_COLUMN_IDX)
            selected_model = name_item.data(Qt.UserRole)
        else:
            selected_model = None
        return selected_model

    def get_selected_template(self):
        """Get currently selected simulation template."""
        index = self.templates_tv.currentIndex()
        if index.isValid():
            current_row = index.row()
            name_item = self.templates_model.item(current_row,
                                                  self.NAME_COLUMN_IDX)
            selected_template = name_item.data(Qt.UserRole)
        else:
            selected_template = None
        return selected_template

    def get_cached_data(self, geojson_name):
        """Get model data that should be cached."""
        cached_file_path = None
        try:
            tc = ThreediCalls(self.threedi_api)
            model_id = self.current_model.id
            if geojson_name == "breaches":
                download = tc.fetch_3di_model_geojson_breaches_download(
                    model_id)
            elif geojson_name == "cells":
                download = tc.fetch_3di_model_geojson_cells_download(model_id)
            else:
                return cached_file_path
            filename = f"{geojson_name}_{model_id}_{download.etag}.json"
            file_path = os.path.join(CACHE_PATH, filename)
            if not file_cached(file_path):
                get_download_file(download, file_path)
            cached_file_path = file_path
            self.communication.bar_info(f"Model {geojson_name} cached.")
        except ApiException as e:
            error_msg = extract_error_message(e)
            if "geojson file not found" in error_msg:
                pass
            else:
                self.communication.bar_error(error_msg)
        except Exception as e:
            logger.exception("Error when getting to-be-cached data")
            error_msg = f"Error: {e}"
            self.communication.bar_error(error_msg)
        return cached_file_path