def __init__(self, parent, msg = ''): ''' progressBar class instatiation method. It creates a QgsMessageBar with provided msg and a working QProgressBar :param parent: :param msg: string ''' self.iface = parent.iface widget = self.iface.messageBar().createMessage("GooGIS plugin:",msg) progressBar = QProgressBar() progressBar.setRange(0,0) #(1,steps) progressBar.setValue(0) progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) widget.layout().addWidget(progressBar) QApplication.processEvents() self.iface.messageBar().pushWidget(widget, Qgis.Info, 50) QApplication.processEvents()
class WaitDialog(IgnoreKeyPressesDialog): def __init__(self, parent, title, progress=False): super().__init__(parent, Qt.WindowTitleHint) vbox = QVBoxLayout() self.progress_bar = QProgressBar() max_val = PROGRESS_BAR_MAX if progress else 0 self.progress_bar.setRange(0, max_val) self.progress_bar.setTextVisible(False) vbox.addWidget(self.progress_bar) self.setModal(True) self.setWindowTitle(title) self.setLayout(vbox) self.setMaximumHeight(0) self.setFixedWidth(parent.width() * 0.25) self.show() def update_progress(self, progress: float) -> None: self.progress_bar.setValue(int(progress * PROGRESS_BAR_MAX)) self.progress_bar.repaint() # otherwise just updates in 1% steps
def create_progress_message_bar(msg_bar, msg, no_percentage=False): """ Use the messageBar of QGIS to display a message describing what's going on (typically during a time-consuming task), and a bar showing the progress of the process. :param msg: Message to be displayed, describing the current task :type: str :returns: progress object on which we can set the percentage of completion of the task through progress.setValue(percentage) :rtype: QProgressBar """ progress_message_bar = msg_bar.createMessage(msg) progress = QProgressBar() if no_percentage: progress.setRange(0, 0) progress_message_bar.layout().addWidget(progress) msg_bar.pushWidget(progress_message_bar, Qgis.Info) return progress_message_bar, progress
class progressBar: def __init__(self, parent, title=''): ''' progressBar class instatiation method. It creates a QgsMessageBar with provided msg and a working QProgressBar :param parent: :param msg: string ''' self.iface = parent.iface self.title = title def start(self, max=0, msg=''): self.widget = self.iface.messageBar().createMessage(self.title, msg) self.progressBar = QProgressBar() self.progressBar.setRange(0, max) self.progressBar.setValue(0) self.progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.widget.layout().addWidget(self.progressBar) QApplication.processEvents() self.iface.messageBar().pushWidget(self.widget, Qgis.Info, 50) QApplication.processEvents() def setProgress(self, value): try: self.progressBar.setValue(value) QApplication.processEvents() except: pass def setMsg(self, msg): self.widget.setText(msg) QApplication.processEvents() def stop(self, msg=''): ''' the progressbar is stopped with a succes message :param msg: string :return: ''' self.iface.messageBar().clearWidgets() message = self.iface.messageBar().createMessage(self.title, msg) self.iface.messageBar().pushWidget(message, Qgis.Info, 2)
def generate_report(self, db, report_type): # Check if mapfish and Jasper are installed, otherwise show where to # download them from and return if not self.report_dependency.check_if_dependency_is_valid(): self.report_dependency.download_dependency(URL_REPORTS_LIBRARIES) return java_home_set = self.java_dependency.set_java_home() if not java_home_set: self.java_dependency.get_java_on_demand() self.logger.info_msg( __name__, QCoreApplication.translate( "ReportGenerator", "Java is a prerequisite. Since it was not found, it is being configured..." )) return plot_layer = self.app.core.get_layer(db, db.names.LC_PLOT_T, load=True) if not plot_layer: return selected_plots = plot_layer.selectedFeatures() if not selected_plots: self.logger.warning_msg( __name__, QCoreApplication.translate( "ReportGenerator", "To generate reports, first select at least a plot!")) return # Where to store the reports? previous_folder = QSettings().value( "Asistente-LADM-COL/reports/save_into_dir", ".") save_into_folder = QFileDialog.getExistingDirectory( None, QCoreApplication.translate( "ReportGenerator", "Select a folder to save the reports to be generated"), previous_folder) if not save_into_folder: self.logger.warning_msg( __name__, QCoreApplication.translate( "ReportGenerator", "You need to select a folder where to save the reports before continuing." )) return QSettings().setValue("Asistente-LADM-COL/reports/save_into_dir", save_into_folder) config_path = os.path.join(DEPENDENCY_REPORTS_DIR_NAME, report_type) json_spec_file = os.path.join(config_path, 'spec_json_file.json') script_name = '' if os.name == 'posix': script_name = 'print' elif os.name == 'nt': script_name = 'print.bat' script_path = os.path.join(DEPENDENCY_REPORTS_DIR_NAME, 'bin', script_name) if not os.path.isfile(script_path): self.logger.warning( __name__, "Script file for reports wasn't found! {}".format(script_path)) return self.enable_action_requested.emit(report_type, False) # Update config file yaml_config_path = self.update_yaml_config(db, config_path) self.logger.debug( __name__, "Config file for reports: {}".format(yaml_config_path)) total = len(selected_plots) step = 0 count = 0 tmp_dir = self.get_tmp_dir() # Progress bar setup progress = QProgressBar() if total == 1: progress.setRange(0, 0) else: progress.setRange(0, 100) progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.app.gui.create_progress_message_bar( QCoreApplication.translate("ReportGenerator", "Generating {} report{}...").format( total, '' if total == 1 else 's'), progress) polygons_with_holes = [] multi_polygons = [] for selected_plot in selected_plots: plot_id = selected_plot[db.names.T_ID_F] geometry = selected_plot.geometry() abstract_geometry = geometry.get() if abstract_geometry.ringCount() > 1: polygons_with_holes.append(str(plot_id)) self.logger.warning( __name__, QCoreApplication.translate( "ReportGenerator", "Skipping Annex 17 for plot with {}={} because it has holes. The reporter module does not support such polygons." ).format(db.names.T_ID_F, plot_id)) continue if abstract_geometry.numGeometries() > 1: multi_polygons.append(str(plot_id)) self.logger.warning( __name__, QCoreApplication.translate( "ReportGenerator", "Skipping Annex 17 for plot with {}={} because it is a multi-polygon. The reporter module does not support such polygons." ).format(db.names.T_ID_F, plot_id)) continue # Generate data file json_file = self.update_json_data(db, json_spec_file, plot_id, tmp_dir, report_type) self.logger.debug(__name__, "JSON file for reports: {}".format(json_file)) # Run sh/bat passing config and data files proc = QProcess() proc.readyReadStandardError.connect( functools.partial(self.stderr_ready, proc=proc)) proc.readyReadStandardOutput.connect( functools.partial(self.stdout_ready, proc=proc)) parcel_number = self.ladm_data.get_parcels_related_to_plots( db, [plot_id], db.names.LC_PARCEL_T_PARCEL_NUMBER_F) or [''] file_name = '{}_{}_{}.pdf'.format(report_type, plot_id, parcel_number[0]) current_report_path = os.path.join(save_into_folder, file_name) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): # Grant execution permissions os.chmod( script_path, stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR | stat.S_IRUSR | stat.S_IRGRP) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): proc = None self.logger.warning( __name__, "Couldn't execute script to generate report...") else: loop = QEventLoop() proc.finished.connect(loop.exit) loop.exec() self.logger.debug(__name__, "{}:{}".format(plot_id, proc.exitCode())) if proc.exitCode() == 0: count += 1 step += 1 progress.setValue(step * 100 / total) os.remove(yaml_config_path) self.enable_action_requested.emit(report_type, True) self.logger.clear_message_bar() if total == count: if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report <a href='file:///{}'>{}</a> was successfully generated!" ).format(normalize_local_url(save_into_folder), file_name) else: msg = QCoreApplication.translate( "ReportGenerator", "All reports were successfully generated in folder <a href='file:///{path}'>{path}</a>!" ).format(path=normalize_local_url(save_into_folder)) self.logger.success_msg(__name__, msg) else: details_msg = '' if polygons_with_holes: details_msg += QCoreApplication.translate( "ReportGenerator", " The following polygons were skipped because they have holes and are not supported: {}." ).format(", ".join(polygons_with_holes)) if multi_polygons: details_msg += QCoreApplication.translate( "ReportGenerator", " The following polygons were skipped because they are multi-polygons and are not supported: {}." ).format(", ".join(multi_polygons)) if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report for plot {} couldn't be generated!{} See QGIS log (tab '{}') for details." ).format(plot_id, details_msg, self.LOG_TAB) else: if count == 0: msg = QCoreApplication.translate( "ReportGenerator", "No report could be generated!{} See QGIS log (tab '{}') for details." ).format(details_msg, self.LOG_TAB) else: msg = QCoreApplication.translate( "ReportGenerator", "At least one report couldn't be generated!{details_msg} See QGIS log (tab '{log_tab}') for details. Go to <a href='file:///{path}'>{path}</a> to see the reports that were generated." ).format(details_msg=details_msg, path=normalize_local_url(save_into_folder), log_tab=self.LOG_TAB) self.logger.warning_msg(__name__, msg)
def generate_report(self, db, button): # Check if mapfish and Jasper are installed, otherwise show where to # download them from and return base_path = os.path.join(os.path.expanduser('~'), 'Asistente-LADM_COL', 'impresion') bin_path = os.path.join(base_path, 'bin') if not os.path.exists(bin_path): self.qgis_utils.message_with_button_download_report_dependency_emitted.emit( QCoreApplication.translate( "ReportGenerator", "The dependency library to generate reports is not installed. Click on the button to download and install it." )) return # Check version required_version_found = True version_path = os.path.join(base_path, 'version') if not os.path.exists(version_path): required_version_found = False else: version_found = '' with open(version_path) as f: version_found = f.read() if version_found != REPORTS_REQUIRED_VERSION: required_version_found = False if not required_version_found: self.qgis_utils.message_with_button_remove_report_dependency_emitted.emit( QCoreApplication.translate( "ReportGenerator", "The dependency library to generate reports was found, but does not match with the version required. Click the button to remove the installed version and try again." )) return # Check if JAVA_HOME path is set, otherwise use path from project Generator if os.name == 'nt': if 'JAVA_HOME' not in os.environ: java_path = self.get_java_path_from_project_generator() if not java_path: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "Please set JAVA_HOME path in Project Generator Settings or in Environmental Variables for your OS" ), Qgis.Warning) return else: os.environ["JAVA_HOME"] = java_path self.log.logMessage( "The JAVA_HOME path has been set using Project Generator Settings for reports.", PLUGIN_NAME, Qgis.Info) plot_layer = self.qgis_utils.get_layer(db, PLOT_TABLE, QgsWkbTypes.PolygonGeometry, load=True) if plot_layer is None: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "Layer 'Plot' not found in DB! {}").format( db.get_description()), Qgis.Warning) return selected_plots = plot_layer.selectedFeatures() if not selected_plots: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "To generate reports, first select at least a plot!"), Qgis.Warning) return # Where to store the reports? previous_folder = QSettings().value( "Asistente-LADM_COL/reports/save_into_dir", ".") save_into_folder = QFileDialog.getExistingDirectory( None, QCoreApplication.translate( "ReportGenerator", "Select a folder to save the reports to be generated"), previous_folder) if not save_into_folder: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "You need to select a folder where to save the reports before continuing." ), Qgis.Warning) return QSettings().setValue("Asistente-LADM_COL/reports/save_into_dir", save_into_folder) config_path = os.path.join(base_path, 'ANT') json_spec_file = os.path.join(config_path, 'spec_json_file.json') script_name = '' if os.name == 'posix': script_name = 'print' elif os.name == 'nt': script_name = 'print.bat' script_path = os.path.join(bin_path, script_name) if not os.path.isfile(script_path): print("### SCRIPT FILE WASN'T FOUND") return button.setEnabled(False) # Update config file yaml_config_path = self.update_yaml_config(db, config_path) print("CONFIG FILE:", yaml_config_path) total = len(selected_plots) step = 0 count = 0 tmp_dir = self.get_tmp_dir() # Progress bar setup progress = QProgressBar() if total == 1: progress.setRange(0, 0) else: progress.setRange(0, 100) progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.qgis_utils.create_progress_message_bar_emitted.emit( QCoreApplication.translate("ReportGenerator", "Generating {} report{}...").format( total, '' if total == 1 else 's'), progress) for selected_plot in selected_plots: plot_id = selected_plot[ID_FIELD] # Generate data file json_file = self.update_json_data(db, json_spec_file, plot_id, tmp_dir) print("JSON FILE:", json_file) # Run sh/bat passing config and data files proc = QProcess() proc.readyReadStandardError.connect( functools.partial(self.stderr_ready, proc=proc)) proc.readyReadStandardOutput.connect( functools.partial(self.stdout_ready, proc=proc)) current_report_path = os.path.join( save_into_folder, 'anexo_17_{}.pdf'.format(plot_id)) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): # Grant execution permissions os.chmod( script_path, stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR | stat.S_IRUSR | stat.S_IRGRP) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): proc = None print("### COULDN'T EXECUTE SCRIPT TO GENERATE REPORT...") else: loop = QEventLoop() proc.finished.connect(loop.exit) loop.exec() print(plot_id, ':', proc.exitCode()) if proc.exitCode() == 0: count += 1 step += 1 progress.setValue(step * 100 / total) os.remove(yaml_config_path) button.setEnabled(True) self.qgis_utils.clear_message_bar_emitted.emit() if total == count: if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report <a href='file://{}'>anexo_17_{}.pdf</a> was successfully generated!" ).format(save_into_folder, plot_id) else: msg = QCoreApplication.translate( "ReportGenerator", "All reports were successfully generated in folder <a href='file://{path}'>{path}</a>!" ).format(path=save_into_folder) self.qgis_utils.message_with_duration_emitted.emit( msg, Qgis.Success, 0) else: if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report for plot {} couldn't be generated! See QGIS log (tab 'Anexo_17') for details." ).format(plot_id) else: if count == 0: msg = QCoreApplication.translate( "ReportGenerator", "No report could be generated! See QGIS log (tab 'Anexo_17') for details." ) else: msg = QCoreApplication.translate( "ReportGenerator", "At least one report couldn't be generated! See QGIS log (tab 'Anexo_17') for details. Go to <a href='file://{path}'>{path}</a> to see the reports that were generated." ).format(path=save_into_folder) self.qgis_utils.message_with_duration_emitted.emit( msg, Qgis.Warning, 0)
def doRemoveNode(self): toolname = "RemoveNode" # check that a layer is selected layer = self.iface.mapCanvas().currentLayer() if not layer: QMessageBox.information(None, toolname, "A topology node layer must be selected") return # check that the selected layer is a postgis one if layer.providerType() != 'postgres': QMessageBox.information(None, toolname, "A PostGIS layer must be selected") return uri = QgsDataSourceUri(layer.source()) # get the layer schema toponame = str(uri.schema()) if not toponame: QMessageBox.information(None, toolname, "Layer " + layer.name() + " doesn't look like a topology node layer.\n(no schema set in datasource)") return; node_id_fno = layer.dataProvider().fieldNameIndex('node_id') if ( node_id_fno < 0 ): QMessageBox.information(None, toolname, "Layer " + layer.name() + " does not have an 'node_id' field (not a topology node layer?)") return # get the selected features errors = [] selected = layer.selectedFeatures() if not selected: QMessageBox.information(None, toolname, "Select the node(s) you want to remove") return msgBar = self.iface.messageBar() pb = QProgressBar( msgBar ) msgBar.pushWidget( pb, Qgis.Info, 5 ) pb.setRange( 0, len(selected) ) pb.setValue( 0 ) conn = psycopg2.connect( str(uri.connectionInfo()) ) for feature in selected: pb.setValue(pb.value()+1) # get its node_id node_id = getIntAttributeByIndex(feature, node_id_fno) try: cur = conn.cursor() cur.execute("SELECT abs((getnodeedges(%s, %s)).edge)", (toponame, node_id)) if cur.rowcount == 2: (edge1_id) = cur.fetchone() (edge2_id) = cur.fetchone() cur.close() if edge1_id != edge2_id: cur = conn.cursor() cur.execute("SELECT ST_ModEdgeHeal(%s, %s, %s)", (toponame, edge1_id, edge2_id)) conn.commit() cur.close() else: errors.append("Node " + str(node_id) + " is the only node in a ring, cannot be removed") elif cur.rowcount == 0: cur.close() cur = conn.cursor() cur.execute("SELECT ST_RemIsoNode(%s, %s)", (toponame, node_id)) conn.commit() cur.close() else: cur.close() errors.append("Node " + str(node_id) + " is not of degree 2 nor isolated") except psycopg2.Error as e: errors.append("Removing node " + str(node_id) + ":\n" + str(e)) conn.commit() cur.close() continue conn.close() removed = len(selected) - len(errors) report = "Removed " + str(removed) + " nodes over " + str(len(selected)) + " selected\n" if errors: report += "\nFailures (" if len(errors) > 5: report += "first 5 of " report += str(len(errors)) + "):\n\n" + "\n".join(errors[:5]) QMessageBox.information(None, toolname, report) layer.removeSelection() self.iface.mapCanvas().refresh()
class OSMDownloaderDialog(QDialog, FORM_CLASS): def __init__(self, iface, startX, startY, endX, endY, parent=None): """Constructor.""" super(OSMDownloaderDialog, 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.iface = iface self.setCoordinates(startX, startY, endX, endY) self.threadpool = QThreadPool() self.size = 0 self.plugin_dir = os.path.dirname(__file__) def setCoordinates(self, startX, startY, endX, endY): if startX < endX: minLong = startX maxLong = endX else: minLong = endX maxLong = startX if startY < endY: minLat = startY maxLat = endY else: minLat = endY maxLat = startY self.wEdit.setText(str(minLong)) self.sEdit.setText(str(minLat)) self.eEdit.setText(str(maxLong)) self.nEdit.setText(str(maxLat)) @pyqtSlot() def on_saveButton_clicked(self): ret = QFileDialog.getSaveFileName( parent=None, caption='Define file name and location', filter='OSM Files(*.osm)') fileName = ret[0] split = fileName.split('.') if len(split) > 0 and split[-1] == 'osm': pass else: fileName += '.osm' self.filenameEdit.setText(fileName) @pyqtSlot() def on_button_box_accepted(self): if self.filenameEdit.text() == '': QMessageBox.warning( self, self.tr("Warning!"), self.tr("Please, select a location to save the file.")) return # Initiating processing osmRequest = OSMRequest(self.filenameEdit.text()) osmRequest.setParameters(self.wEdit.text(), self.sEdit.text(), self.eEdit.text(), self.nEdit.text()) # Connecting end signal osmRequest.signals.processFinished.connect(self.processFinished) osmRequest.signals.sizeReported.connect(self.reportSize) osmRequest.signals.proxyOpened.connect(self.proxy) osmRequest.signals.errorOccurred.connect(self.errorOccurred) osmRequest.signals.userCanceled.connect(self.userCanceled) # Setting the progress bar # << Updated by SIGMOÉ self.msgBar = self.iface.messageBar() self.progressMessageBar = self.msgBar.createMessage( 'Downloading data...') # >> self.progressBar = QProgressBar() self.progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.progressMessageBar.layout().addWidget(self.progressBar) self.iface.messageBar().pushWidget(self.progressMessageBar, Qgis.Info) self.progressBar.setRange(0, 0) self.progressMessageBar.destroyed.connect(osmRequest.signals.cancel) # Starting process self.threadpool.start(osmRequest) @pyqtSlot(str) def proxy(self, proxy): self.progressMessageBar.setText('Proxy set to: ' + proxy) @pyqtSlot(str) def errorOccurred(self, message): QMessageBox.warning(self, 'Fatal!', message) self.close() @pyqtSlot() def userCanceled(self): QMessageBox.warning(self, 'Info!', 'Process canceled by user!') self.close() @pyqtSlot(float) def reportSize(self, size): self.size = size self.progressMessageBar.setText('Downloading: ' + "{0:.2f}".format(size) + ' megabytes from OSM servers...') @pyqtSlot(str) def processFinished(self, message): self.progressBar.setRange(0, 100) self.progressBar.setValue(100) self.progressMessageBar.setText('Downloaded ' + "{0:.2f}".format(self.size) + ' megabytes in total from OSM servers') if self.checkBox.isChecked(): # << Updated by SIGMOÉ # Add each OSM layer with specific style lyr_types = [['multipolygons', 'polygon'], ['multilinestrings', 'line'], ['lines', 'line'], ['points', 'point']] for lt in lyr_types: lyr = self.iface.addVectorLayer( self.filenameEdit.text() + '|layername=' + lt[0], 'osm', 'ogr') style = "styles/osm_mapnik_" + lt[1] + ".qml" qml_file = os.path.join(self.plugin_dir, style) lyr.loadNamedStyle(qml_file) # >> QMessageBox.warning(self, 'Info!', message) # << Updated by SIGMOÉ self.msgBar.clearWidgets() # >> self.close()
class ProjectFromOSMDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, iface): QtWidgets.QDialog.__init__(self) self.iface = iface self.setupUi(self) self.path = standard_path() self.error = None self.report = [] self.worker_thread = None self.running = False self.bbox = None self.json = [] self.project = None self.logger = logging.getLogger("aequilibrae") self._run_layout = QGridLayout() # Area to import network for self.choose_place = QRadioButton() self.choose_place.setText("Place name") self.choose_place.toggled.connect(self.change_place_type) self.choose_place.setChecked(False) self.choose_canvas = QRadioButton() self.choose_canvas.setText("Current map canvas area") self.choose_canvas.setChecked(True) self.place = QLineEdit() self.place.setVisible(False) self.source_type_frame = QVBoxLayout() self.source_type_frame.setAlignment(Qt.AlignLeft) self.source_type_frame.addWidget(self.choose_place) self.source_type_frame.addWidget(self.choose_canvas) self.source_type_frame.addWidget(self.place) self.source_type_widget = QGroupBox('Target') self.source_type_widget.setLayout(self.source_type_frame) # Buttons and output self.but_choose_output = QPushButton() self.but_choose_output.setText("Choose file output") self.but_choose_output.clicked.connect(self.choose_output) self.output_path = QLineEdit() self.but_run = QPushButton() self.but_run.setText("Import network and create project") self.but_run.clicked.connect(self.run) self.buttons_frame = QVBoxLayout() self.buttons_frame.addWidget(self.but_choose_output) self.buttons_frame.addWidget(self.output_path) self.buttons_frame.addWidget(self.but_run) self.buttons_widget = QWidget() self.buttons_widget.setLayout(self.buttons_frame) self.progressbar = QProgressBar() self.progress_label = QLabel() self.update_widget = QWidget() self.update_frame = QVBoxLayout() self.update_frame.addWidget(self.progressbar) self.update_frame.addWidget(self.progress_label) self.update_widget.setLayout(self.update_frame) self.update_widget.setVisible(False) self._run_layout.addWidget(self.source_type_widget) self._run_layout.addWidget(self.buttons_widget) self._run_layout.addWidget(self.update_widget) self.setLayout(self._run_layout) self.resize(280, 250) def choose_output(self): new_name, file_type = GetOutputFileName(self, '', ["SQLite database(*.sqlite)"], ".sqlite", self.path) if new_name is not None: self.output_path.setText(new_name) def run(self): self.update_widget.setVisible(True) self.resize(280, 300) if self.choose_canvas.isChecked(): self.report.append( reporter('Chose to download network for canvas area')) e = self.iface.mapCanvas().extent() bbox = [e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum()] else: self.progress_label.setText('Establishing area for download') self.report.append(reporter('Chose to download network for place')) bbox, r = placegetter(self.place.text()) self.report.extend(r) if bbox is None: self.leave() return west, south, east, north = bbox[0], bbox[1], bbox[2], bbox[3] self.report.append( reporter( 'Downloading network for bounding box ({} {}, {}, {})'.format( west, south, east, north))) self.bbox = bbox surveybox = QgsRectangle(QgsPointXY(west, south), QgsPointXY(east, north)) geom = QgsGeometry().fromRect(surveybox) conv = QgsDistanceArea() area = conv.convertAreaMeasurement(conv.measureArea(geom), QgsUnitTypes.AreaSquareMeters) self.report.append( reporter( 'Area for which we will download a network: {:,} km.sq'.format( area / 1000000))) if area <= max_query_area_size: geometries = [[west, south, east, north]] else: parts = math.ceil(area / max_query_area_size) horizontal = math.ceil(math.sqrt(parts)) vertical = math.ceil(parts / horizontal) dx = east - west dy = north - south geometries = [] for i in range(horizontal): xmin = west + i * dx xmax = west + (i + 1) * dx for j in range(vertical): ymin = south + j * dy ymax = south + (j + 1) * dy box = [xmin, ymin, xmax, ymax] geometries.append(box) p = Parameters().parameters modes = [list(k.keys())[0] for k in p['network']['modes']] self.progress_label.setText('Downloading data') self.downloader = OSMDownloader(geometries, modes) self.run_download_thread() def final_steps(self): self.project = Project(self.output_path.text(), True) self.project.network.create_empty_tables() curr = self.project.conn.cursor() curr.execute("""ALTER TABLE links ADD COLUMN osm_id integer""") curr.execute("""ALTER TABLE nodes ADD COLUMN osm_id integer""") self.project.conn.commit() self.project.conn.close() self.builder = OSMBuilder(self.downloader.json, self.project.source) self.run_thread() def run_download_thread(self): self.downloader.downloading.connect(self.signal_downloader_handler) self.downloader.start() self.exec_() def run_thread(self): self.builder.building.connect(self.signal_handler) self.builder.start() self.exec_() def change_place_type(self): if self.choose_place.isChecked(): self.place.setVisible(True) else: self.place.setVisible(False) def leave(self): self.close() dlg2 = ReportDialog(self.iface, self.report) dlg2.show() dlg2.exec_() def signal_downloader_handler(self, val): if val[0] == "Value": self.progressbar.setValue(val[1]) elif val[0] == "maxValue": self.progressbar.setRange(0, val[1]) elif val[0] == "text": self.progress_label.setText(val[1]) elif val[0] == "FinishedDownloading": self.final_steps() def signal_handler(self, val): if val[0] == "Value": self.progressbar.setValue(val[1]) elif val[0] == "maxValue": self.progressbar.setRange(0, val[1]) elif val[0] == "text": self.progress_label.setText(val[1]) elif val[0] == "finished_threaded_procedure": self.project = Project(self.output_path.text()) self.progress_label.setText('Adding spatial indices') self.project.network.add_spatial_index() self.project.network.add_triggers() l = self.project.network.count_links() n = self.project.network.count_nodes() self.report.append(reporter(f'{l:,} links generated')) self.report.append(reporter(f'{n:,} nodes generated')) self.leave()
def generate_report(self, db, report_type): # Check if mapfish and Jasper are installed, otherwise show where to # download them from and return base_path = os.path.join(os.path.expanduser('~'), 'Asistente-LADM_COL', 'impresion') bin_path = os.path.join(base_path, 'bin') if not os.path.exists(bin_path): self.qgis_utils.message_with_button_download_report_dependency_emitted.emit( QCoreApplication.translate( "ReportGenerator", "The dependency library to generate reports is not installed. Click on the button to download and install it." )) return # Check version required_version_found = True version_path = os.path.join(base_path, 'version') if not os.path.exists(version_path): required_version_found = False else: version_found = '' with open(version_path) as f: version_found = f.read() if version_found.strip() != REPORTS_REQUIRED_VERSION: required_version_found = False if not required_version_found: self.qgis_utils.message_with_button_remove_report_dependency_emitted.emit( QCoreApplication.translate( "ReportGenerator", "The dependency library to generate reports was found, but does not match with the version required. Click the button to remove the installed version and try again." )) return # Check if JAVA_HOME path is set, otherwise use path from QGIS Model Baker if os.name == 'nt': if 'JAVA_HOME' not in os.environ: self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Information) self.msg.setText( QCoreApplication.translate( "ReportGenerator", "JAVA_HOME environment variable is not defined, please define it as an enviroment variable on Windows and restart QGIS before generating the annex 17." )) self.msg.setWindowTitle( QCoreApplication.translate("ReportGenerator", "JAVA_HOME not defined")) self.msg.setStandardButtons(QMessageBox.Close) self.msg.exec_() return plot_layer = self.qgis_utils.get_layer(db, PLOT_TABLE, QgsWkbTypes.PolygonGeometry, load=True) if plot_layer is None: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "Layer 'Plot' not found in DB! {}").format( db.get_description()), Qgis.Warning) return selected_plots = plot_layer.selectedFeatures() if not selected_plots: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "To generate reports, first select at least a plot!"), Qgis.Warning) return # Where to store the reports? previous_folder = QSettings().value( "Asistente-LADM_COL/reports/save_into_dir", ".") save_into_folder = QFileDialog.getExistingDirectory( None, QCoreApplication.translate( "ReportGenerator", "Select a folder to save the reports to be generated"), previous_folder) if not save_into_folder: self.qgis_utils.message_emitted.emit( QCoreApplication.translate( "ReportGenerator", "You need to select a folder where to save the reports before continuing." ), Qgis.Warning) return QSettings().setValue("Asistente-LADM_COL/reports/save_into_dir", save_into_folder) config_path = os.path.join(base_path, report_type) json_spec_file = os.path.join(config_path, 'spec_json_file.json') script_name = '' if os.name == 'posix': script_name = 'print' elif os.name == 'nt': script_name = 'print.bat' script_path = os.path.join(bin_path, script_name) if not os.path.isfile(script_path): print("### SCRIPT FILE WASN'T FOUND") return self.enable_action_requested.emit(report_type, False) # Update config file yaml_config_path = self.update_yaml_config(db, config_path) print("CONFIG FILE:", yaml_config_path) total = len(selected_plots) step = 0 count = 0 tmp_dir = self.get_tmp_dir() # Progress bar setup progress = QProgressBar() if total == 1: progress.setRange(0, 0) else: progress.setRange(0, 100) progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.qgis_utils.create_progress_message_bar_emitted.emit( QCoreApplication.translate("ReportGenerator", "Generating {} report{}...").format( total, '' if total == 1 else 's'), progress) polygons_with_holes = [] multi_polygons = [] for selected_plot in selected_plots: plot_id = selected_plot[ID_FIELD] geometry = selected_plot.geometry() abstract_geometry = geometry.get() if abstract_geometry.ringCount() > 1: polygons_with_holes.append(str(plot_id)) self.log.logMessage( QCoreApplication.translate( "ReportGenerator", "Skipping Annex 17 for plot with {}={} because it has holes. The reporter module does not support such polygons." ).format(ID_FIELD, plot_id), PLUGIN_NAME, Qgis.Warning) continue if abstract_geometry.numGeometries() > 1: multi_polygons.append(str(plot_id)) self.log.logMessage( QCoreApplication.translate( "ReportGenerator", "Skipping Annex 17 for plot with {}={} because it is a multi-polygon. The reporter module does not support such polygons." ).format(ID_FIELD, plot_id), PLUGIN_NAME, Qgis.Warning) continue # Generate data file json_file = self.update_json_data(db, json_spec_file, plot_id, tmp_dir, report_type) print("JSON FILE:", json_file) # Run sh/bat passing config and data files proc = QProcess() proc.readyReadStandardError.connect( functools.partial(self.stderr_ready, proc=proc)) proc.readyReadStandardOutput.connect( functools.partial(self.stdout_ready, proc=proc)) parcel_number = self.ladm_data.get_parcels_related_to_plot( db, plot_id, PARCEL_NUMBER_FIELD) or [''] file_name = '{}_{}_{}.pdf'.format(report_type, plot_id, parcel_number[0]) current_report_path = os.path.join(save_into_folder, file_name) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): # Grant execution permissions os.chmod( script_path, stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR | stat.S_IRUSR | stat.S_IRGRP) proc.start(script_path, [ '-config', yaml_config_path, '-spec', json_file, '-output', current_report_path ]) if not proc.waitForStarted(): proc = None print("### COULDN'T EXECUTE SCRIPT TO GENERATE REPORT...") else: loop = QEventLoop() proc.finished.connect(loop.exit) loop.exec() print(plot_id, ':', proc.exitCode()) if proc.exitCode() == 0: count += 1 step += 1 progress.setValue(step * 100 / total) os.remove(yaml_config_path) self.enable_action_requested.emit(report_type, True) self.qgis_utils.clear_message_bar_emitted.emit() if total == count: if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report <a href='file:///{}'>{}</a> was successfully generated!" ).format(normalize_local_url(save_into_folder), file_name) else: msg = QCoreApplication.translate( "ReportGenerator", "All reports were successfully generated in folder <a href='file:///{path}'>{path}</a>!" ).format(path=normalize_local_url(save_into_folder)) self.qgis_utils.message_with_duration_emitted.emit( msg, Qgis.Success, 0) else: details_msg = '' if polygons_with_holes: details_msg += QCoreApplication.translate( "ReportGenerator", " The following polygons were skipped because they have holes and are not supported: {}." ).format(", ".join(polygons_with_holes)) if multi_polygons: details_msg += QCoreApplication.translate( "ReportGenerator", " The following polygons were skipped because they are multi-polygons and are not supported: {}." ).format(", ".join(multi_polygons)) if total == 1: msg = QCoreApplication.translate( "ReportGenerator", "The report for plot {} couldn't be generated!{} See QGIS log (tab '{}') for details." ).format(plot_id, details_msg, self.LOG_TAB) else: if count == 0: msg = QCoreApplication.translate( "ReportGenerator", "No report could be generated!{} See QGIS log (tab '{}') for details." ).format(details_msg, self.LOG_TAB) else: msg = QCoreApplication.translate( "ReportGenerator", "At least one report couldn't be generated!{details_msg} See QGIS log (tab '{log_tab}') for details. Go to <a href='file:///{path}'>{path}</a> to see the reports that were generated." ).format(details_msg=details_msg, path=normalize_local_url(save_into_folder), log_tab=self.LOG_TAB) self.qgis_utils.message_with_duration_emitted.emit( msg, Qgis.Warning, 0)
class ProgressDialog(QDialog): """ Progress dialog shows progress bar for algorithm. """ def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.workerThread = None self.state = False self.outputLoc = None self.resultStatus = None self.reRun = False self.savedProj = None # Build GUI Elements self.setWindowTitle("SEILAPLAN wird ausgeführt") self.resize(500, 100) self.container = QVBoxLayout() self.progressBar = QProgressBar(self) self.progressBar.setMinimumWidth(500) self.statusLabel = QLabel(self) self.hbox = QHBoxLayout() self.cancelButton = QDialogButtonBox() self.closeButton = QDialogButtonBox() self.resultLabel = ClickLabel(self) self.resultLabel.setMaximumWidth(500) self.resultLabel.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)) self.resultLabel.setWordWrap(True) self.rerunButton = QPushButton("Berechnungen wiederholen") self.rerunButton.setVisible(False) spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.cancelButton.setStandardButtons(QDialogButtonBox.Cancel) self.cancelButton.clicked.connect(self.onAbort) self.closeButton.setStandardButtons(QDialogButtonBox.Close) self.closeButton.clicked.connect(self.onClose) self.hbox.addWidget(self.rerunButton) self.hbox.addItem(spacer) self.hbox.addWidget(self.cancelButton) self.hbox.setAlignment(self.cancelButton, Qt.AlignHCenter) self.hbox.addWidget(self.closeButton) self.hbox.setAlignment(self.closeButton, Qt.AlignHCenter) self.closeButton.hide() self.container.addWidget(self.progressBar) self.container.addWidget(self.statusLabel) self.container.addWidget(self.resultLabel) self.container.addLayout(self.hbox) self.container.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.container) def setThread(self, workerThread): self.workerThread = workerThread self.connectProgressSignals() def connectProgressSignals(self): # Connet signals of thread self.workerThread.sig_jobEnded.connect(self.jobEnded) self.workerThread.sig_jobError.connect(self.onError) self.workerThread.sig_value.connect(self.valueFromThread) self.workerThread.sig_range.connect(self.rangeFromThread) self.workerThread.sig_text.connect(self.textFromThread) self.workerThread.sig_result.connect(self.resultFromThread) self.rerunButton.clicked.connect(self.onRerun) def run(self): # Show modal dialog window (QGIS is still responsive) self.show() # start event loop self.exec_() def jobEnded(self, success): self.setWindowTitle("SEILAPLAN") if success: self.statusLabel.setText("Berechnungen abgeschlossen.") self.progressBar.setValue(self.progressBar.maximum()) self.setFinalMessage() else: # If there was an abort by the user self.statusLabel.setText("Berechnungen abgebrochen.") self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() self.rerunButton.setVisible(True) def valueFromThread(self, value): self.progressBar.setValue(value) def rangeFromThread(self, range_vals): self.progressBar.setRange(range_vals[0], range_vals[1]) def maxFromThread(self, max): self.progressBar.setValue(self.progressBar.maximum()) def textFromThread(self, value): self.statusLabel.setText(value) def resultFromThread(self, result): [self.outputLoc, self.resultStatus] = result def setFinalMessage(self): self.resultLabel.clicked.connect(self.onResultClicked) self.resultLabel.blockSignals(True) linkToFolder = ('<html><head/><body><p></p><p><a href=' '"file:////{0}"><span style="text-decoration: ' 'underline; color:#0000ff;">{0}</span></a></p>' '</body></html>'.format(self.outputLoc)) # Optimization successful if self.resultStatus == 1: self.resultLabel.setText(textOK + linkToFolder) self.resultLabel.blockSignals(False) # Cable takes off of support elif self.resultStatus == 2: self.resultLabel.setText(textSeil + linkToFolder) self.resultLabel.blockSignals(False) # Optimization partially successful elif self.resultStatus == 3: self.resultLabel.setText(textHalf + linkToFolder) self.resultLabel.blockSignals(False) # Optimization not successful elif self.resultStatus == 4: self.resultLabel.setText(textBad) self.setLayout(self.container) def onResultClicked(self): # Open a folder window if sys.platform == 'darwin': # MAC subprocess.call(["open", "-R", self.outputLoc]) elif sys.platform.startswith('linux'): # LINUX subprocess.Popen(["xdg-open", self.outputLoc]) elif 'win32' in sys.platform: # WINDOWS from subprocess import CalledProcessError try: subprocess.check_call(['explorer', self.outputLoc]) except CalledProcessError: pass def onAbort(self): self.setWindowTitle("SEILAPLAN") self.statusLabel.setText("Laufender Prozess wird abgebrochen...") self.workerThread.cancel() # Terminates process cleanly def onError(self, exception_string): self.setWindowTitle("SEILAPLAN: Berechnung fehlgeschlagen") self.statusLabel.setText("Ein Fehler ist aufgetreten:") self.resultLabel.setText(exception_string) self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() def onRerun(self): self.reRun = True self.onClose() def finallyDo(self): self.cancelButton.hide() self.closeButton.show() def onClose(self): self.close()
class ProgressDialog(QDialog): """ Progress dialog shows progress bar for algorithm. """ def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.workerThread = None self.state = False self.resultStatus = None self.doReRun = False self.wasCanceled = False self.wasSuccessful = False self.savedProj = None self.result = None self.messageTxt = { 'msg_optimierung': self.tr('Berechnung der optimalen Stuetzenpositionen...'), 'msg_seillinie': self.tr('Berechnung der optimale Seillinie...') } # Build GUI Elements self.setWindowTitle(self.tr("SEILAPLAN wird ausgefuehrt")) self.resize(500, 100) self.container = QVBoxLayout() self.progressBar = QProgressBar(self) self.progressBar.setMinimumWidth(500) self.statusLabel = QLabel(self) self.hbox = QHBoxLayout() self.cancelButton = QDialogButtonBox() self.closeButton = QDialogButtonBox() self.resultLabel = QLabel(self) self.resultLabel.setMaximumWidth(500) self.resultLabel.setSizePolicy( QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)) self.resultLabel.setWordWrap(True) spacer1 = QSpacerItem(20, 20, QSizePolicy.Fixed, QSizePolicy.Fixed) self.rerunButton = QPushButton(self.tr("zurueck zum Startfenster")) self.rerunButton.setVisible(False) spacer2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.cancelButton.setStandardButtons(QDialogButtonBox.Cancel) self.cancelButton.button(QDialogButtonBox.Cancel).setText(self.tr("Abbrechen")) self.cancelButton.clicked.connect(self.onAbort) self.closeButton.setStandardButtons(QDialogButtonBox.Close) self.closeButton.button(QDialogButtonBox.Close).setText(self.tr("Schliessen")) self.closeButton.clicked.connect(self.onClose) self.hbox.addWidget(self.rerunButton) self.hbox.addItem(spacer2) self.hbox.addWidget(self.cancelButton) self.hbox.setAlignment(self.cancelButton, Qt.AlignHCenter) self.hbox.addWidget(self.closeButton) self.hbox.setAlignment(self.closeButton, Qt.AlignHCenter) self.closeButton.hide() self.container.addWidget(self.progressBar) self.container.addWidget(self.statusLabel) self.container.addWidget(self.resultLabel) self.container.addItem(spacer1) self.container.addLayout(self.hbox) self.container.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.container) # noinspection PyMethodMayBeStatic def tr(self, message, **kwargs): """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 Parameters ---------- **kwargs """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(type(self).__name__, message) def setThread(self, workerThread): self.workerThread = workerThread self.connectThreadSignals() def connectThreadSignals(self): # Connect signals of thread self.workerThread.sig_jobEnded.connect(self.jobEnded) self.workerThread.sig_jobError.connect(self.onError) self.workerThread.sig_value.connect(self.valueFromThread) self.workerThread.sig_range.connect(self.rangeFromThread) self.workerThread.sig_text.connect(self.textFromThread) self.workerThread.sig_result.connect(self.resultFromThread) self.rerunButton.clicked.connect(self.onRerun) def run(self): # Show modal dialog window (QGIS is still responsive) self.show() # start event loop self.exec() def jobEnded(self, success): self.setWindowTitle("SEILAPLAN") if success: self.progressBar.setValue(self.progressBar.maximum()) self.wasSuccessful = True # Close progress dialog so that adjustment window can be opened self.close() else: # If there was an abort by the user self.statusLabel.setText(self.tr("Berechnungen abgebrochen.")) self.progressBar.setValue(self.progressBar.minimum()) self.finallyDo() def valueFromThread(self, value): self.progressBar.setValue(int(value)) def rangeFromThread(self, range_vals): self.progressBar.setRange(int(round(range_vals[0])), int(round(range_vals[1]))) def maxFromThread(self, max): self.progressBar.setValue(self.progressBar.maximum()) def textFromThread(self, message): self.statusLabel.setText(self.messageTxt[message]) def resultFromThread(self, resultStatus): self.resultStatus = resultStatus # resultStatus: # 1 = Optimization successful # 2 = Cable takes off from support # 3 = Optimization partially successful def onAbort(self): self.setWindowTitle('SEILAPLAN') self.statusLabel.setText(self.tr( 'Laufender Prozess wird abgebrochen...')) self.workerThread.cancel() # Terminates process cleanly self.wasCanceled = True def onError(self, exception_string): self.setWindowTitle(self.tr('SEILAPLAN: Berechnung fehlgeschlagen')) self.statusLabel.setText(self.tr('Ein Fehler ist aufgetreten:')) self.resultLabel.setText(self.tr(exception_string)) self.resultLabel.setHidden(False) self.progressBar.setValue(self.progressBar.minimum()) self.setLayout(self.container) self.finallyDo() def onRerun(self): self.doReRun = True self.onClose() def finallyDo(self): self.rerunButton.setVisible(True) self.cancelButton.hide() self.closeButton.show() def onClose(self): self.close()
class IDESCaliWebServices: def __init__(self, iFace): self.iFace = iFace self.plugin_dir = os.path.dirname(__file__) locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'IDESCaliWebServices_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) self.dlg = IDESCaliWebServicesDialog() self.dlginfo = InfoDialog() self.generatedService = None self.bar = QProgressBar() self.bar.setRange(0, 0) self.bar.setGeometry(950, 500, 200, 25) self.actions = [] self.menu = self.tr(u'&Servicios WMS - Geoportal IDESC') self.first_start = None def tr(self, message): return QCoreApplication.translate('IDESCaliWebServices', 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): 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.iFace.addToolBarIcon(action) if add_to_menu: self.iFace.addPluginToWebMenu(self.menu, action) self.actions.append(action) return action def initGui(self): self.add_all_action() self.dlg.table_widget.itemSelectionChanged.connect(self.updateDesc) self.dlg.help_button.clicked.connect(self.openDlgInfo) self.dlg.close_button.clicked.connect(self.closeDlg) self.dlg.search_box.textEdited.connect(self.search) self.dlg.add_button.released.connect(self.loadWebService) self.dlginfo.ok_dialog.released.connect(self.closeAbout) self.first_start = True def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iFace.removePluginWebMenu( self.tr(u'&Servicios WMS - Geoportal IDESC'), action) self.iFace.removeToolBarIcon(action) def run(self): if self.generatedService.web_map_service is not None: self.dlg.search_box.clear() self.fill_table(self.generatedService.web_map_service.contents) self.dlg.show() result = self.dlg.exec_() if result: pass def run_0(self): self.loadServiceList(Service.IDESCaliDataset.value) self.run() def add_all_action(self): icon_path = ':/plugins/idescali_ws/icon.png' self.add_action(icon_path, text=self.tr( service_text_map[Service.IDESCaliDataset.value]), callback=self.run_0, whats_this=str(Service.IDESCaliDataset.value), parent=self.iFace.mainWindow()) def loadServiceList(self, service_id: int): self.generatedService = WebMapServiceClass(service_id) url = self.generatedService.service_url self.bar.show() if self.generatedService.service_type == ServiceType.WebMapService.value: try: wms = WebMapService(url) self.generatedService.setWebMapService(wms) except Exception as e: QMessageBox.information( None, "ERROR:", 'No se puede cargar este servicio en este momento.' + str(e)) elif self.generatedService.service_type == ServiceType.WebMapTileService.value: try: wmts = WebMapTileService(url) self.generatedService.setWebMapService(wmts) except Exception as e: QMessageBox.information( None, "ERROR:", 'No se puede acceder a este servicio en este momento.' + str(e)) self.bar.close() def openDlgInfo(self): self.dlginfo.show() def closeDlg(self): self.generatedService = None self.dlg.search_box.clear() self.dlg.table_widget.setRowCount(0) self.dlg.table_widget.setColumnCount(0) self.dlg.layer_name_box.clear() self.dlg.close() if self.dlginfo: self.dlginfo.close() def closeAbout(self): if self.dlginfo: self.dlginfo.close() def fill_table(self, contentOrderedDict): self.dlg.table_widget.setRowCount(0) count = self.dlg.table_widget.rowCount() self.dlg.table_widget.setColumnCount(4) for content in contentOrderedDict: index = count name = contentOrderedDict[content].name title = contentOrderedDict[content].title abstract = contentOrderedDict[content].abstract self.dlg.table_widget.insertRow(index) self.dlg.table_widget.setItem(index, 1, QTableWidgetItem(str(name))) self.dlg.table_widget.setItem(index, 2, QTableWidgetItem(str(title))) self.dlg.table_widget.setItem(index, 3, QTableWidgetItem(str(abstract))) self.dlg.table_widget.setHorizontalHeaderLabels( ["ID", "Capa", "Nombre", "Resumen"]) self.dlg.label_conteo.setText("Capas disponibles: " + str(len(contentOrderedDict))) self.setTableWidgetBehaviour() def setTableWidgetBehaviour(self): self.dlg.table_widget.setColumnWidth(0, 0) self.dlg.table_widget.setColumnWidth(1, 200) self.dlg.table_widget.setColumnWidth(2, 200) self.dlg.table_widget.setColumnWidth(3, 200) self.dlg.table_widget.horizontalHeader().setSectionResizeMode( QHeaderView.Fixed) self.dlg.table_widget.verticalHeader().setSectionResizeMode( QHeaderView.Fixed) self.dlg.table_widget.setSelectionBehavior( QAbstractItemView.SelectRows) self.dlg.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dlg.table_widget.setSelectionMode( QAbstractItemView.ExtendedSelection) def search(self): criteria = self.dlg.search_box.text() criteria = criteria.lower() wms_filtered_contents = OrderedDict() contents = self.generatedService.web_map_service.contents for content in contents: name = contents[content].name if criteria in name: wms_filtered_contents[content] = contents[content] self.fill_table(wms_filtered_contents) def getSelectedItemsFromTable(self): rowNames = [] selected = self.dlg.table_widget.selectedItems() if len(selected) > 0: for i in range(0, len(selected), 4): row = self.dlg.table_widget.row(selected[i]) name = self.dlg.table_widget.item(row, 1).text() rowNames.append(name) selectedServices = OrderedDict() contents = self.generatedService.web_map_service.contents for rowName in rowNames: for content in contents: name_itr = contents[content].name if name_itr == rowName: selectedServices[content] = contents[content] return selectedServices def updateDesc(self): try: selectedServices = self.getSelectedItemsFromTable() self.dlg.layer_name_box.clear() names = '' for selectedService in selectedServices: name_itr = selectedServices[selectedService].name names += name_itr + ',' names = names[:-1] self.dlg.layer_name_box.setText(names) self.dlg.layer_name_box.setReadOnly(True) except: QgsMessageLog.logMessage( "No selecciono ninguna capa WMS para cargar") def loadWebService(self): # get selected items and add to the map self.bar.show() EPSG_CODE_4326 = 'EPSG:4326' selectedServices = self.getSelectedItemsFromTable() web_map_service = self.generatedService.web_map_service for selectedService in selectedServices: if self.generatedService.service_url is not None: layer_name = selectedServices[selectedService].name url = 'contextualWMSLegend=0' if hasattr(web_map_service[layer_name], 'crsOptions'): if len(web_map_service[layer_name].crsOptions) > 0: if EPSG_CODE_4326 in web_map_service[ layer_name].crsOptions: url += '&crs=' + EPSG_CODE_4326 if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + EPSG_CODE_4326 else: url += '&crs=' + web_map_service[ layer_name].crsOptions[0] if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + web_map_service[ layer_name].crsOptions[0] else: url += '&crs=' + EPSG_CODE_4326 if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + EPSG_CODE_4326 url += '&dpiMode=7&featureCount=10&format=image/png&styles' + \ '&layers=' + layer_name + \ '&url=' + str(self.generatedService.service_url) rlayer = QgsRasterLayer( url, selectedServices[selectedService].title, 'wms') if not rlayer.isValid(): QMessageBox.information( None, "ERROR:", 'Imposible cargar las capas ' + selectedServices[selectedService].title + ' en este momento.') else: QgsProject.instance().addMapLayer(rlayer) self.iFace.messageBar().pushMessage( "Mensaje:", "Fueron cargadas las capas WMS con exito", level=Qgis.Success, duration=3) else: QMessageBox.information( None, "ERROR:", 'No selecciono ninguna capa WMS para cargar') self.bar.close()
class OSMDownloaderDialog(QDialog, FORM_CLASS): def __init__(self, iface, startX, startY, endX, endY, parent=None): """Constructor.""" super(OSMDownloaderDialog, 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.iface = iface self.setCoordinates(startX, startY, endX, endY) self.threadpool = QThreadPool() self.size = 0 def setCoordinates(self, startX, startY, endX, endY): if startX < endX: minLong = startX maxLong = endX else: minLong = endX maxLong = startX if startY < endY: minLat = startY maxLat = endY else: minLat = endY maxLat = startY self.wEdit.setText(str(minLong)) self.sEdit.setText(str(minLat)) self.eEdit.setText(str(maxLong)) self.nEdit.setText(str(maxLat)) @pyqtSlot() def on_saveButton_clicked(self): ret = QFileDialog.getSaveFileName(parent=None, caption='Define file name and location', filter='OSM Files(*.osm)') fileName = ret[0] split = fileName.split('.') if len(split)>0 and split[-1] == 'osm': pass else: fileName += '.osm' self.filenameEdit.setText(fileName) @pyqtSlot() def on_button_box_accepted(self): if self.filenameEdit.text() == '': QMessageBox.warning(self, self.tr("Warning!"), self.tr("Please, select a location to save the file.")) return # Initiating processing osmRequest = OSMRequest(self.filenameEdit.text()) osmRequest.setParameters(self.wEdit.text(), self.sEdit.text(), self.eEdit.text(), self.nEdit.text()) # Connecting end signal osmRequest.signals.processFinished.connect(self.processFinished) osmRequest.signals.sizeReported.connect(self.reportSize) osmRequest.signals.proxyOpened.connect(self.proxy) osmRequest.signals.errorOccurred.connect(self.errorOccurred) osmRequest.signals.userCanceled.connect(self.userCanceled) # Setting the progress bar self.progressMessageBar = self.iface.messageBar().createMessage('Downloading data...') self.progressBar = QProgressBar() self.progressBar.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) self.progressMessageBar.layout().addWidget(self.progressBar) self.iface.messageBar().pushWidget(self.progressMessageBar, Qgis.Info) self.progressBar.setRange(0, 0) self.progressMessageBar.destroyed.connect(osmRequest.signals.cancel) # Starting process self.threadpool.start(osmRequest) @pyqtSlot(str) def proxy(self, proxy): self.progressMessageBar.setText('Proxy set to: '+proxy) @pyqtSlot(str) def errorOccurred(self, message): QMessageBox.warning(self, 'Fatal!', message) self.close() @pyqtSlot() def userCanceled(self): QMessageBox.warning(self, 'Info!', 'Process canceled by user!') self.close() @pyqtSlot(float) def reportSize(self, size): self.size = size self.progressMessageBar.setText('Downloading: '+"{0:.2f}".format(size)+' megabytes from OSM servers...') @pyqtSlot(str) def processFinished(self, message): self.progressBar.setRange(0, 100) self.progressBar.setValue(100) self.progressMessageBar.setText('Downloaded '+"{0:.2f}".format(self.size)+' megabytes in total from OSM servers') QMessageBox.warning(self, 'Info!', message) if self.checkBox.isChecked(): self.iface.addVectorLayer(self.filenameEdit.text(), 'osm_data', 'ogr') self.close()
class Isogeo: """Isogeo plugin for QGIS LTR.""" # attributes plg_version = plg_tools.plugin_metadata(base_path=plg_basepath) logger.info("\n\n\t========== Isogeo Search Engine for QGIS ==========") logger.info("OS: {0}".format(platform.platform())) logger.info("QGIS Version: {0}".format(Qgis.QGIS_VERSION)) logger.info("Plugin version: {0}".format(plg_version)) logger.info("Log level: {0}".format(log_level)) # Screens resolution screens_count = QDesktopWidget().screenCount() for screenNbr in range(screens_count): sizeObject = QDesktopWidget().screenGeometry(screenNbr) logger.info("Screen: {}/{} - Size: {}x{}".format( screenNbr + 1, screens_count, sizeObject.height(), sizeObject.width())) del screens_count, sizeObject def __init__(self, iface): """Constructor. :param QgsInterface 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. """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = Path(__file__).parent # requiered `_auth` subfolder plg_authdir = Path(plg_basepath) / "_auth" if not plg_authdir.exists(): plg_authdir.mkdir() else: pass # initialize locale try: locale = str(qsettings.value("locale/userLocale", "fr", type=str))[0:2] except TypeError as exc: logger.error( "Bad type in QSettings: {}. Original error: {}".format( type(qsettings.value("locale/userLocale")), exc)) locale = "fr" # load localized translation locale_path = (self.plugin_dir / "i18n" / "isogeo_search_engine_{}.qm".format(locale)) logger.info("Language applied: {0}".format(locale)) if locale_path.exists(): self.translator = QTranslator() self.translator.load(str(locale_path)) if qVersion() > "4.3.3": QCoreApplication.installTranslator(self.translator) else: pass else: pass if locale == "fr": self.lang = "fr" else: self.lang = "en" # Declare instance attributes self.actions = [] self.menu = self.tr("&Isogeo") self.toolbar = self.iface.addToolBar("Isogeo") self.toolbar.setObjectName("Isogeo") self.pluginIsActive = False self.form_mng = None # UI submodules self.credits_dialog = IsogeoCredits() # SUBMODULES # instanciating self.informer = UserInformer(message_bar=msgBar, trad=self.tr) self.md_display = MetadataDisplayer() self.md_display.tr = self.tr self.approps_mng = SharesParser() self.approps_mng.tr = self.tr self.authenticator = Authenticator() self.api_requester = ApiRequester() self.api_requester.tr = self.tr self.form_mng = SearchFormManager(self.tr) self.form_mng.qs_mng.url_builder = self.api_requester.build_request_url self.form_mng.qs_mng.lang = self.lang # connecting self.api_requester.api_sig.connect(self.token_slot) self.api_requester.api_sig.connect(self.informer.request_slot) self.api_requester.search_sig.connect(self.search_slot) self.api_requester.details_sig.connect( self.md_display.show_complete_md) self.api_requester.shares_sig.connect(self.approps_mng.send_share_info) self.authenticator.auth_sig.connect(self.auth_slot) self.authenticator.ask_shares.connect(self.shares_slot) self.approps_mng.shares_ready.connect(self.write_shares_info) self.approps_mng.shares_ready.connect(self.informer.shares_slot) # start variables self.savedSearch = str self.loopCount = 0 self.hardReset = False self.showResult = False self.showDetails = False self.store = False self.PostGISdict = self.form_mng.results_mng.build_postgis_dict( qsettings) self.old_text = "" self.page_index = 1 self.json_path = plg_basepath / "_user/quicksearches.json" # noinspection PyMethodMayBeStatic def tr(self, message, context="Isogeo"): """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 :context: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate(context, message) def add_action( self, ico_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 str 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. :param str text: Text that should be shown in menu items for this action. :param function callback: Function to be called when the action is triggered. :param bool enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :param bool add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :param bool add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :param str status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :param QWidget parent: Parent widget for the new action. Defaults None. :param str 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 """ ico = QIcon(ico_path) action = QAction(ico, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) action.setToolTip("Isogeo (v{})".format(self.plg_version)) 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.""" ico_path = ":/plugins/Isogeo/icon.png" self.add_action( ico_path, text=self.tr("Search within Isogeo catalogs"), callback=self.run, parent=self.iface.mainWindow(), ) # ------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed.""" # save base portal URL in qsettings qsettings.setValue("isogeo/settings/portal_base_url", self.form_mng.input_portal_url.text()) # save cache self.form_mng.results_mng.cache_mng.dumper() # disconnects self.form_mng.closingPlugin.disconnect(self.onClosePlugin) # remove this statement if dockwidget is to remain # for reuse if plugin is reopened # Commented next statement since it causes QGIS crashe # when closing the docked window: self.form_mng = None self.pluginIsActive = False def unload(self): """Remove the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginWebMenu(self.tr("&Isogeo"), action) try: self.iface.mainWindow().statusBar().removeWidget(self.bar) except: pass self.iface.removeToolBarIcon(action) self.form_mng = None logger.handlers = [] # remove the toolbar del self.toolbar # --- AUTHENTICATION ------------------------------------------------------ def user_authentication(self): """Test the validity of the user id and secret. This is the first major function the plugin calls when executed. It retrieves the id and secret from the config file. If they are set to their default value, it asks for them. If not, it tries to send a request. """ self.savedSearch = "first" self.form_mng.switch_widgets_on_and_off(0) api_init = self.authenticator.manage_api_initialization() if api_init[0]: self.api_requester.setup_api_params(api_init[1]) else: pass def auth_slot(self, auth_signal: str): if auth_signal == "ok": self.user_authentication() else: self.authenticator.ui_auth_form.btn_ok_cancel.buttons( )[0].setEnabled(False) pass def token_slot(self, token_signal: str): """ Slot connected to ApiRequester.api_sig signal emitted when a response to a token request has been received from Isogeo's API or when the content of a response to any type of request can't be parsed. The 'api_sig' parameter correspond to the string passed by ApiRequester.handle_reply method (see modules/api/request.py). The value of this parameter depend on the response's content received from Isogeo's API. :param str token_signal: a string passed by the signal whose value determines what will be done. Options : - "ok" : Authentication has succeeded, the token is stored so it sends a search request to the API. - "creds_issue" : User's credentials are wrong so it displays the authentication form to provide good ones. - "NoInternet" : Asks to user to check his Internet connection. """ if token_signal == "ok": if self.savedSearch == "first": logger.debug("First search since plugin started.") self.authenticator.first_auth = False self.shares_slot() else: self.api_requester.send_request() elif token_signal == "creds_issue": self.authenticator.ui_auth_form.btn_ok_cancel.buttons( )[0].setEnabled(False) else: self.pluginIsActive = False def shares_slot(self): logger.debug("Asking application properties to the Isogeo API.") self.api_requester.send_request(request_type="shares") # --- SEARCH -------------------------------------------------------------- def search(self, show: bool = False, page_change: int = 0): """ Slot connected to signals emitted by 'advances search', 'order' or 'keywords' comboboxes, also by 'show results', 'next page' or 'previous page' buttons when a user interacts with one of them. It retrieves the selected parameters and establishes the corresponding URL, and then sends a search request to the API, by calling ApiRequester.send_request(). :param bool show: True if the 'show results', 'next page' or 'previous page' button was pressed :param int page_change: -1 if 'previous page' button was pressed, 1 if 'next page' button was pressed, 0 otherwise """ logger.debug( "Search function called. Building the url that is to be sent" "to the API") # Disabling all user inputs during the search function is running self.form_mng.switch_widgets_on_and_off(0) if self.store is True: # Store the previous search name = self.tr("Last search") self.form_mng.qs_mng.write_params(name, "Last") # update quick searches combobox saved_searches = list(self.form_mng.qs_mng.load_file()) self.form_mng.pop_qs_cbbs(items_list=saved_searches) self.store = False else: pass # STORING ALL THE INFORMATIONS NEEDED TO BUILD THE URL # Widget position params = self.form_mng.save_params() # Info for _limit parameter if show is True: # Adding the loading bar self.add_loading_bar() self.showResult = True params["show"] = True else: params["show"] = False # Info for _offset parameter if page_change != 0: if page_change < 0 and self.page_index > 1: self.page_index -= 1 elif page_change > 0 and self.page_index < plg_tools.results_pages_counter( total=self.results_count): self.page_index += 1 else: return False else: self.page_index = 1 params["page"] = self.page_index # Info for _lang parameter params["lang"] = self.lang # URL BUILDING FUNCTION CALLED. self.api_requester.currentUrl = self.api_requester.build_request_url( params) # Sending the request to Isogeo API self.api_requester.send_request() return def search_slot(self, result: dict, tags: dict): """ Slot connected to ApiRequester.search_sig signal. It updates widgets, using SearchFormManager appropiate methods to fill them from 'tags' parameter and put them in the right status. It also display the results contained in 'result' parameter by calling ResultManager.show_results method if necessary. :param dict result: parsed content of API's reply to a search request (passed by ApiRequester.handle_reply method) :param dict tags: tags contained in API's reply parsed and classed into a dict (passed by ApiRequester.handle_reply method) """ QgsMessageLog.logMessage( message="Query sent & received: {}".format(result.get("query")), tag="Isogeo", level=0, ) # Save entered text and filters in search form self.form_mng.old_text = self.form_mng.txt_input.text() params = self.form_mng.save_params() # Show how many results there are self.results_count = result.get("total") self.form_mng.btn_show.setText( str(self.results_count) + self.tr(" results")) page_count = str( plg_tools.results_pages_counter(total=self.results_count)) self.form_mng.lbl_page.setText("page " + str(self.page_index) + self.tr(" on ") + page_count) # Clear widgets self.form_mng.tbl_result.clearContents() self.form_mng.tbl_result.setRowCount(0) # Initialize the widgets that dont't need to be updated if self.savedSearch == "_default" or self.hardReset is True: logger.debug("Default search or reset.") self.form_mng.init_steps() else: logger.debug("Not default search nor reset.") pass # Filling Advanced search comboboxes from tags self.form_mng.pop_as_cbbs(tags) # Filling quick searches comboboxes from json file (also the one in settings tab) qs_list = list(self.form_mng.qs_mng.load_file().keys()) self.form_mng.pop_qs_cbbs(qs_list) # Sorting Advanced search comboboxes for cbb in self.cbbs_search_advanced: cbb.model().sort(0) # Putting comboboxes' selected index to the appropriate location # and updating key words checkable combobox if self.hardReset is True: # In case of a hard reset, we don't have to worry about comboboxes' selected index logger.debug("Reset search") self.form_mng.update_cbb_keywords( tags_keywords=tags.get("keywords")) else: logger.debug("Classical search or quicksearch (no reset search)") if self.savedSearch == "": # Putting all the comboboxes selected index to their previous location. logger.debug("Classic search case (not quicksearch)") # Setting widgets to their previous index self.form_mng.set_ccb_index(params=params) # Updating the keywords special combobox (filling + indexing) self.form_mng.update_cbb_keywords( tags_keywords=tags.get("keywords"), selected_keywords=params.get("keys"), ) else: # Putting all the comboboxes selected index # according to params found in the json file logger.debug("Quicksearch case: {}".format(self.savedSearch)) # Opening the json to get quick search's params search_params = self.form_mng.qs_mng.load_file().get( self.savedSearch) # Putting widgets to their previous states according to the json content self.form_mng.set_ccb_index(params=search_params, quicksearch=self.savedSearch) self.savedSearch = "" # Updating the keywords special combobox (filling + indexing) keywords_list = [ v for k, v in search_params.items() if k.startswith("keyword") ] self.form_mng.update_cbb_keywords( tags_keywords=tags.get("keywords"), selected_keywords=keywords_list) # tweaking plg_tools._ui_tweaker( ui_widgets=self.form_mng.tab_search.findChildren(QComboBox)) # Formatting show result button according to the number of results if self.results_count == 0: self.form_mng.btn_show.setEnabled(False) self.form_mng.btn_show.setStyleSheet("QPushButton { }") else: self.form_mng.btn_show.setEnabled(True) self.form_mng.btn_show.setStyleSheet( "QPushButton " "{background-color: rgb(255, 144, 0); color: white}") # Showing result : if button 'show result', 'next page' or 'previous page' pressed if self.showResult is True: self.form_mng.fill_tbl_result( content=result, page_index=self.page_index, results_count=self.results_count, ) iface.mainWindow().statusBar().removeWidget(self.bar) self.store = True else: pass # Re enable all user input fields now the search function is # finished. self.form_mng.switch_widgets_on_and_off(1) # Reseting attributes values self.hardReset = False self.showResult = False def set_widget_status(self): """Set a few variable and send the request to Isogeo API.""" selected_search = self.form_mng.cbb_quicksearch_use.currentText() logger.debug("Quicksearch selected: {}".format(selected_search)) # load quicksearches saved_searches = self.form_mng.qs_mng.load_file() if selected_search != self.tr("Quicksearches"): self.form_mng.switch_widgets_on_and_off(0) # disable search form # check if selected search can be found if selected_search in saved_searches: self.savedSearch = selected_search search_params = saved_searches.get(selected_search) logger.debug( "Quicksearch found in saved searches and" " related search params have just been loaded from.") elif selected_search not in saved_searches and "_default" in saved_searches: logger.warning("Selected search ({}) not found." "'_default' will be used instead.") self.savedSearch = "_default" search_params = saved_searches.get("_default") else: logger.error( "Selected search ({}) and '_default' do not exist.".format( selected_search)) return # Check projection settings in loaded search params if "epsg" in search_params: epsg = int(plg_tools.get_map_crs().split(":")[1]) logger.debug( "Specific SRS found in search params: {}".format(epsg)) if epsg == search_params.get("epsg"): canvas = iface.mapCanvas() e = search_params.get("extent") rect = QgsRectangle(e[0], e[1], e[2], e[3]) canvas.setExtent(rect) canvas.refresh() else: canvas = iface.mapCanvas() canvas.mapRenderer().setProjectionsEnabled(True) canvas.mapRenderer().setDestinationCrs( QgsCoordinateReferenceSystem( search_params.get("epsg"), QgsCoordinateReferenceSystem.EpsgCrsId, )) e = search_params.get("extent") rect = QgsRectangle(e[0], e[1], e[2], e[3]) canvas.setExtent(rect) canvas.refresh() # load request self.api_requester.currentUrl = search_params.get("url") self.api_requester.send_request() else: if self.savedSearch == "first": logger.debug("First search. Launch '_default' search.") self.savedSearch = "_default" search_params = saved_searches.get("_default") self.api_requester.currentUrl = search_params.get("url") self.api_requester.send_request() else: logger.debug("No quicksearch selected.") def edited_search(self): """On the Qline edited signal, decide weither a search has to be launched.""" try: logger.debug("Editing finished signal sent.") except AttributeError: pass if self.form_mng.txt_input.text() == self.old_text: logger.debug("The lineEdit text hasn't changed." " So pass without sending a request.") else: logger.debug("The line Edit text changed." " Calls the search function.") if self.form_mng.txt_input.text() == "Ici c'est Isogeo !": plg_tools.special_search("isogeo") self.form_mng.txt_input.clear() return elif self.form_mng.txt_input.text() == "Picasa": plg_tools.special_search("picasa") self.form_mng.txt_input.clear() return else: pass self.search() def reinitialize_search(self): """Clear all widget, putting them all back to their default value. Clear all widget and send a request to the API (which ends up updating the fields : send_request() calls handle_reply(), which calls update_fields()) """ logger.debug("Reset search function called.") self.hardReset = True # clear widgets self.form_mng.reinit_widgets() # launch search self.search() # -- UTILS ---------------------------------------------------------------- def add_loading_bar(self): """Display a progress bar.""" self.bar = QProgressBar() self.bar.setRange(0, 0) self.bar.setFixedWidth(120) self.iface.mainWindow().statusBar().insertPermanentWidget(0, self.bar) def send_details_request(self, md_id): """Send a request for aditionnal info about one data. :param str md_id: UUID of metadata to retrieve """ logger.debug("Full metatada sheet asked. Building the url.") self.api_requester.currentUrl = "{}/resources/{}{}".format( self.api_requester.api_url_base, md_id, "?_include=conditions,contacts,coordinate-system,events," "feature-attributes,limitations,keywords,specifications", ) self.api_requester.send_request("details") def write_shares_info(self, text: str): """Write informations about the shares in the Settings pannel. See: #87 :param text str: share informations from Isogeo API """ if text != "no_shares": logger.info("Displaying application properties.") self.authenticator.ui_auth_form.btn_ok_cancel.buttons( )[0].setEnabled(True) self.form_mng.txt_shares.setText(text) if self.savedSearch == "first": self.set_widget_status() else: pass else: self.pluginIsActive = False # method ending return # -- LAUNCH PAD------------------------------------------------------------ # This function is launched when the plugin is activated. def run(self): """Run method that loads and starts the plugin.""" if not self.pluginIsActive: logger.info("Opening (display) the plugin...") self.pluginIsActive = True # dockwidget may not exist if: # first run of plugin # removed on close (see self.onClosePlugin method) if self.form_mng is None: # Create the dockwidget (after translation) and keep reference self.form_mng = SearchFormManager(self.tr) self.form_mng.qs_mng.url_builder = self.api_requester.build_request_url self.form_mng.qs_mng.lang = self.lang logger.debug("Plugin load time: {}".format( plugin_times.get(plg_reg_name, "NR"))) else: pass # connect to provide cleanup on closing of dockwidget self.form_mng.closingPlugin.connect(self.onClosePlugin) # show the dockwidget # TODO: fix to allow choice of dock location self.iface.addDockWidget(Qt.RightDockWidgetArea, self.form_mng) self.form_mng.show() else: pass # Fixing a qgis.core bug that shows a warning banner "connexion time # out" whenever a request is sent (even successfully) See : # http://gis.stackexchange.com/questions/136369/download-file-from-network-using-pyqgis-2-x#comment299999_136427 # msgBar.widgetAdded.connect(msgBar.clearWidgets) """ --- CONNECTING UI WIDGETS <-> FUNCTIONS --- """ # shortcuts self.cbbs_search_advanced = self.form_mng.grp_filters.findChildren( QComboBox) # -- Search form ------------------------------------------------------ # search terms text input self.form_mng.txt_input.editingFinished.connect(self.edited_search) # reset search button self.form_mng.btn_reinit.pressed.connect(self.reinitialize_search) # filters comboboxes self.form_mng.cbb_contact.activated.connect(self.search) self.form_mng.cbb_format.activated.connect(self.search) self.form_mng.cbb_geofilter.activated.connect(self.search) self.form_mng.cbb_inspire.activated.connect(self.search) self.form_mng.cbb_license.activated.connect(self.search) self.form_mng.cbb_owner.activated.connect(self.search) self.form_mng.cbb_srs.activated.connect(self.search) self.form_mng.cbb_type.activated.connect(self.search) self.form_mng.kw_sig.connect(self.search) # -- Results table ---------------------------------------------------- # show and order results self.form_mng.btn_show.pressed.connect(partial(self.search, show=True)) self.form_mng.cbb_ob.activated.connect(partial(self.search, show=True)) self.form_mng.cbb_od.activated.connect(partial(self.search, show=True)) # pagination self.form_mng.btn_next.pressed.connect( partial(self.search, show=True, page_change=1)) self.form_mng.btn_previous.pressed.connect( partial(self.search, show=True, page_change=-1)) # metadata display self.form_mng.results_mng.md_asked.connect(self.send_details_request) # -- Quicksearches ---------------------------------------------------- # select and use self.form_mng.cbb_quicksearch_use.activated.connect( self.set_widget_status) # # -- Settings tab - Search -------------------------------------------- # button to empty the cache of filepaths #135 self.form_mng.btn_cache_trash.pressed.connect( self.form_mng.results_mng.cache_mng.cleaner) # -- Settings tab - Application authentication ------------------------ # Change user -> see below for authentication form self.form_mng.btn_change_user.pressed.connect( self.authenticator.display_auth_form) # share text window self.form_mng.txt_shares.setOpenLinks(False) self.form_mng.txt_shares.anchorClicked.connect(plg_tools.open_webpage) # -- Settings tab - Resources ----------------------------------------- # report and log - see #53 and #139 self.form_mng.btn_log_dir.setIcon(ico_log) self.form_mng.btn_log_dir.pressed.connect( partial(plg_tools.open_dir_file, target=plg_logdir)) self.form_mng.btn_report.pressed.connect( partial( plg_tools.open_webpage, link="https://github.com/isogeo/isogeo-plugin-qgis/issues/new?" "assignees=&template=bug_report.md&title={}" " - plugin v{} QGIS {} ({})&labels=bug&milestone=4".format( self.tr("TITLE ISSUE REPORTED"), plg_tools.plugin_metadata(base_path=plg_basepath), Qgis.QGIS_VERSION, platform.platform(), ), )) # help button self.form_mng.btn_help.pressed.connect( partial(plg_tools.open_webpage, link="http://help.isogeo.com/qgis/")) # view credits - see: #52 self.form_mng.btn_credits.pressed.connect(self.credits_dialog.show) # -- Settings tab - Isogeo Portal settings ------------------------ self.form_mng.input_portal_url.setText( qsettings.value("isogeo/settings/portal_base_url")) """ ------- EXECUTED AFTER PLUGIN IS LAUNCHED --------------------- """ self.form_mng.setWindowTitle("Isogeo - {}".format(self.plg_version)) # add translator method in others modules plg_tools.tr = self.tr self.authenticator.tr = self.tr self.authenticator.lang = self.lang # checks plg_tools.check_proxy_configuration() # 22 self.form_mng.cbb_chck_kw.setEnabled( plg_tools.test_qgis_style()) # see #137 # self.form_mng.cbb_chck_kw.setMaximumSize(QSize(250, 25)) self.form_mng.txt_input.setFocus() # load cache file self.form_mng.results_mng.cache_mng.loader() # connect limitations checker to user informer self.form_mng.results_mng.lim_checker.lim_sig.connect( self.informer.lim_slot) # launch authentication self.user_authentication()
class BhuvanWebServices: def __init__(self, iFace): # Save reference to the QGIS interface self.iFace = iFace # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join( self.plugin_dir, 'i18n', 'BhuvanWebServices_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) self.dlg = BhuvanWebServicesDialog() self.dlginfo = InfoDialog() self.generatedService = None self.bar = QProgressBar() self.bar.setRange(0, 0) self.bar.setGeometry(950, 500, 200, 25) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Bhuvan Web Services') # Check if plugin was started the first time in current QGIS session # Must be set in initGui() to survive plugin reloads self.first_start = None # noinspection PyMethodMayBeStatic def tr(self, message): # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('BhuvanWebServices', 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: # Adds plugin icon to Plugins toolbar self.iFace.addToolBarIcon(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.add_all_action() self.dlg.table_widget.itemSelectionChanged.connect(self.updateDesc) self.dlg.help_button.clicked.connect(self.openDlgInfo) self.dlg.close_button.clicked.connect(self.closeDlg) self.dlg.search_box.textEdited.connect(self.search) self.dlg.add_button.released.connect(self.loadWebService) # will be set False in run() self.first_start = True def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iFace.removePluginWebMenu( self.tr(u'&Bhuvan Web Services'), action) self.iFace.removeToolBarIcon(action) def run(self): if self.generatedService.web_map_service is not None: self.dlg.search_box.clear() self.fill_table(self.generatedService.web_map_service.contents) self.dlg.show() result = self.dlg.exec_() if result: pass def run_0(self): self.loadServiceList(Service.BhuvanPanchayat.value) self.run() def run_1(self): self.loadServiceList(Service.LULC205KDataset.value) self.run() def run_2(self): self.loadServiceList(Service.BhuvanV1WMS.value) self.run() def run_3(self): self.loadServiceList(Service.BhuvanV2WMS.value) self.run() def run_4(self): self.loadServiceList(Service.BhuvanV1WMTS.value) self.run() def run_5(self): self.loadServiceList(Service.BhuvanV2WMTS.value) self.run() def add_all_action(self): icon_path = ':/plugins/bhuvan_web_services/icon.png' self.add_action(icon_path, text=self.tr(service_text_map[Service.BhuvanPanchayat.value]), callback=self.run_0, whats_this=str(Service.BhuvanPanchayat.value), parent=self.iFace.mainWindow()) self.add_action(icon_path, text=self.tr(service_text_map[Service.LULC205KDataset.value]), callback=self.run_1, whats_this=str(Service.LULC205KDataset.value), parent=self.iFace.mainWindow()) self.add_action(icon_path, text=self.tr(service_text_map[Service.BhuvanV1WMS.value]), callback=self.run_2, whats_this=str(Service.BhuvanV1WMS.value), parent=self.iFace.mainWindow()) self.add_action(icon_path, text=self.tr(service_text_map[Service.BhuvanV2WMS.value]), callback=self.run_3, whats_this=str(Service.BhuvanV2WMS.value), parent=self.iFace.mainWindow()) self.add_action(icon_path, text=self.tr(service_text_map[Service.BhuvanV1WMTS.value]), callback=self.run_4, whats_this=str(Service.BhuvanV1WMTS.value), parent=self.iFace.mainWindow()) self.add_action(icon_path, text=self.tr(service_text_map[Service.BhuvanV2WMTS.value]), callback=self.run_5, whats_this=str(Service.BhuvanV2WMTS.value), parent=self.iFace.mainWindow()) def loadServiceList(self, service_id: int): self.iFace.messageBar().pushMessage('Info: ', 'Please wait loading layers ... ', level=Qgis.Info) self.bar.show() self.iFace.mainWindow().repaint() self.generatedService = WebMapServiceClass(service_id) url = self.generatedService.service_url if self.generatedService.service_type == ServiceType.WebMapService.value: try: wms = WebMapService(url) self.generatedService.setWebMapService(wms) except Exception as e: QMessageBox.information(None, "ERROR:", 'Unable to load this service now.' + str(e)) elif self.generatedService.service_type == ServiceType.WebMapTileService.value: try: wmts = WebMapTileService(url) self.generatedService.setWebMapService(wmts) except Exception as e: QMessageBox.information(None, "ERROR:", 'Unable to load this service now.' + str(e)) self.bar.close() def openDlgInfo(self): self.dlginfo.show() def closeDlg(self): self.generatedService = None self.dlg.search_box.clear() self.dlg.table_widget.setRowCount(0) self.dlg.table_widget.setColumnCount(0) self.dlg.layer_name_box.clear() self.dlg.close() if self.dlginfo: self.dlginfo.close() def fill_table(self, contentOrderedDict): self.dlg.table_widget.setRowCount(0) count = self.dlg.table_widget.rowCount() self.dlg.table_widget.setColumnCount(4) for content in contentOrderedDict: index = count # id_int = int(contentOrderedDict[content].index[2:]) name = contentOrderedDict[content].name title = contentOrderedDict[content].title abstract = contentOrderedDict[content].abstract self.dlg.table_widget.insertRow(index) # inserts a blank row # lets fill that row: # self.dlg.table_widget.setItem(index, 0, QTableWidgetItem(str(id_int))) # fills in with the ID self.dlg.table_widget.setItem(index, 1, QTableWidgetItem(str(name))) # fills in with the Name self.dlg.table_widget.setItem(index, 2, QTableWidgetItem(str(title))) # fills in with the Title self.dlg.table_widget.setItem(index, 3, QTableWidgetItem(str(abstract))) # fills in with Abstract self.dlg.table_widget.setHorizontalHeaderLabels(["ID", "Name", "Title", "Abstract"]) self.setTableWidgetBehaviour() def setTableWidgetBehaviour(self): # set row and column sizes and lock them self.dlg.table_widget.setColumnWidth(0, 0) self.dlg.table_widget.setColumnWidth(1, 200) self.dlg.table_widget.setColumnWidth(2, 200) self.dlg.table_widget.setColumnWidth(3, 200) self.dlg.table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) self.dlg.table_widget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) self.dlg.table_widget.setSelectionBehavior(QAbstractItemView.SelectRows) self.dlg.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dlg.table_widget.setSelectionMode(QAbstractItemView.ExtendedSelection) def search(self): criteria = self.dlg.search_box.text() criteria = criteria.lower() wms_filtered_contents = OrderedDict() contents = self.generatedService.web_map_service.contents for content in contents: name = contents[content].name if criteria in name: wms_filtered_contents[content] = contents[content] self.fill_table(wms_filtered_contents) def getSelectedItemsFromTable(self): rowNames = [] selected = self.dlg.table_widget.selectedItems() if len(selected) > 0: for i in range(0, len(selected), 4): row = self.dlg.table_widget.row(selected[i]) name = self.dlg.table_widget.item(row, 1).text() rowNames.append(name) selectedServices = OrderedDict() contents = self.generatedService.web_map_service.contents for rowName in rowNames: for content in contents: name_itr = contents[content].name if name_itr == rowName: selectedServices[content] = contents[content] return selectedServices def updateDesc(self): selectedServices = self.getSelectedItemsFromTable() self.dlg.layer_name_box.clear() names = '' for selectedService in selectedServices: name_itr = selectedServices[selectedService].name names += name_itr + ',' names = names[:-1] self.dlg.layer_name_box.setText(names) self.dlg.layer_name_box.setReadOnly(True) def loadWebService(self): # get selected items and add to the map self.bar.show() EPSG_CODE_4326 = 'EPSG:4326' selectedServices = self.getSelectedItemsFromTable() web_map_service = self.generatedService.web_map_service for selectedService in selectedServices: if self.generatedService.service_url is not None: layer_name = selectedServices[selectedService].name url = 'contextualWMSLegend=0' if hasattr(web_map_service[layer_name], 'crsOptions'): if len(web_map_service[layer_name].crsOptions) > 0: if EPSG_CODE_4326 in web_map_service[layer_name].crsOptions: url += '&crs=' + EPSG_CODE_4326 if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + EPSG_CODE_4326 else: url += '&crs=' + web_map_service[layer_name].crsOptions[0] if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + web_map_service[layer_name].crsOptions[0] else: url += '&crs=' + EPSG_CODE_4326 if self.generatedService.service_type == ServiceType.WebMapTileService.value: url += '&tileMatrixSet=' + EPSG_CODE_4326 url += '&dpiMode=7&featureCount=10&format=image/png&styles' + \ '&layers=' + layer_name + \ '&url=' + str(self.generatedService.service_url) rlayer = QgsRasterLayer(url, selectedServices[selectedService].title, 'wms') if not rlayer.isValid(): QMessageBox.information(None, "ERROR:", 'Unable to load ' + selectedServices[selectedService].title + ' this layer now.') else: QgsProject.instance().addMapLayer(rlayer) else: QMessageBox.information(None, "ERROR:", 'Service url is None') self.bar.close()