def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.jobs_view.setModel(proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) btn.setMinimumSize(QSize(75, 0)) btn.setMaximumSize(QSize(150, 16777215)) self.jobs_view.setIndexWidget(proxy_model.index(row, 5), btn) #self.jobs_view.widget(proxy_model.index(row, 2)).setTextAlignment(Qt.AlignCenter) # self.jobs_view.setMinimumSize(QSize(0, 100)) # self.jobs_view.setColumnWidth(1, 400) # self.jobs_view.setColumnWidth(2, 200) # self.jobs_view.setColumnWidth(3, 200) # self.jobs_view.setColumnWidth(4, 200) # self.jobs_view.setColumnWidth(5, 200) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed)
def setup_class_table(self, classes): default_codes = sorted( [c['Initial_Code'] for c in self.default_classes]) input_codes = sorted([c['Initial_Code'] for c in classes]) new_codes = [c for c in input_codes if c not in default_codes] missing_codes = [c for c in default_codes if c not in input_codes] if len(new_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the definition file do not appear in the chosen data file." .format(', '.join([str(c) for c in new_codes]), None))) if len(missing_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the data file do not appear in the chosen definition file." .format(', '.join([str(c) for c in missing_codes]), None))) # Setup a new classes list with the new class codes for all classes # included in default calsses, and and any other class codes that are # missing added from the default class list classes = [c for c in classes if c['Initial_Code'] in default_codes] classes.extend([ c for c in self.default_classes if c['Initial_Code'] not in input_codes ]) table_model = AggTableModel(classes, parent=self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.remap_view.setModel(proxy_model) # Add selector in cell for row in range(0, len(classes)): lc_classes = QtWidgets.QComboBox() lc_classes.currentIndexChanged.connect(self.lc_class_combo_changed) # Add the classes in order of their codes lc_classes.addItems( sorted(list(self.final_classes.keys()), key=lambda k: self.final_classes[k])) ind = lc_classes.findText(classes[row]['Final_Label']) if ind != -1: lc_classes.setCurrentIndex(ind) self.remap_view.setIndexWidget( proxy_model.index(row, self.remap_view.model().columnCount() - 1), lc_classes) self.remap_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self.remap_view.setColumnWidth(1, 450) self.remap_view.horizontalHeader().setStretchLastSection(True) return True
def update_data_table(self): table_model = DataTableModel(self.datasets, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.data_view.setModel(proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.datasets)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.data_view.setIndexWidget(proxy_model.index(row, 7), btn) self.data_view.horizontalHeader().setResizeMode( QtWidgets.QHeaderView.ResizeToContents) self.data_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows)
def searchModel(self, columnIndex, columnValue): ''' Searches for 'columnValue' in the column whose index is specified by 'columnIndex' in all rows contained in the model. ''' if isinstance(columnValue, QVariant): columnValue = str(columnValue.toString()) if not isinstance(columnValue, str): columnValue = str(columnValue) columnValue = columnValue.strip() proxy = QSortFilterProxyModel(self) proxy.setSourceModel(self._tableModel) proxy.setFilterKeyColumn(columnIndex) proxy.setFilterFixedString(columnValue) # Will return model index containing the primary key. matchingIndex = proxy.mapToSource(proxy.index(0, 0)) return matchingIndex
class NafiDockWidget(QtWidgets.QDockWidget, Ui_NafiDockWidgetBase): closingPlugin = pyqtSignal() def __init__(self, parent=None): """Constructor.""" super(NafiDockWidget, self).__init__(parent) self.setupUi(self) # set up QTreeView self.treeView.setHeaderHidden(True) self.treeView.setSortingEnabled(True) self.treeView.setFocusPolicy(Qt.NoFocus) self.treeView.pressed.connect(self.treeViewPressed) # set up search signal self.lineEdit.textChanged.connect(self.searchTextChanged) self.searchText = "" # set up clear search self.clearSearchButton.clicked.connect(self.clearSearch) # set up About … dialog self.aboutButton.clicked.connect(self.showAboutDialog) # set up Download NAFI data … link button self.dataButton.clicked.connect( lambda: webbrowser.open(getNafiDataUrl())) # set up base model self.treeViewModel = NafiTreeViewModel(getNafiUrl()) # set up proxy model for filtering self.proxyModel = QSortFilterProxyModel(self.treeView) self.proxyModel.setSourceModel(self.treeViewModel) self.proxyModel.setRecursiveFilteringEnabled(True) self.treeView.setModel(self.proxyModel) self.reader = NafiCapabilitiesReader() self.reader.capabilitiesDownloaded.connect( lambda xml: self.initModel(xml)) # restore the view from source whenever this dock widget is made visible again self.visibilityChanged.connect( lambda visible: visible and self.loadNafiWms()) # initialise proxied tree view model from WMS contents # self.loadNafiWms() def loadNafiWms(self): """Load the NAFI WMS and additional layers.""" self.wmsUrl = getNafiUrl() self.reader.downloadCapabilities(self.wmsUrl) def initModel(self, wmsXml): """Initialise a QStandardItemModel from the NAFI WMS.""" googSat = GoogleXyzItem() googHyb = GoogleXyzItem("y") googStr = GoogleXyzItem("m") # ibraWms = IbraWmsItem() ozTopoWmts = OzTopoWmtsItem() self.treeViewModel.loadWms( self.wmsUrl, wmsXml, additionalItems=[googSat, googHyb, googStr, ozTopoWmts]) # set default sort and expansion self.proxyModel.sort(0, Qt.AscendingOrder) self.expandTopLevel() def expandTopLevel(self): # expand the top level items for row in range(self.proxyModel.rowCount()): self.treeView.expand(self.proxyModel.index(row, 0)) def treeViewPressed(self, index): """Load a NAFI WMS layer given an index in the tree view.""" assert isinstance( index, QModelIndex), "Supplied parameter is not a QModelIndex" realIndex = self.proxyModel.mapToSource(index) modelNode = self.treeViewModel.itemFromIndex(realIndex) # if we've got a layer and not a layer group, add to map if modelNode is not None: if isinstance( modelNode, (GoogleXyzItem, IbraWmsItem, OzTopoWmtsItem, WmsItem)): modelNode.addLayer() def searchTextChanged(self, text): """Process a change in the search filter text.""" # user adding characters and has exceeded 3 or more, or is removing characters if len(text) >= 3 or len(self.searchText) > len(text): regex = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxyModel.setFilterRegExp(regex) self.treeView.expandAll() # update last search text state self.searchText = text def clearSearch(self): """Clear search data.""" self.lineEdit.setText(None) self.treeView.collapseAll() def sizeHint(self): return QtCore.QSize(150, 400) def showAboutDialog(self): """Show an About … dialog.""" aboutDialog = NafiAboutDialog() aboutDialog.exec_() def closeEvent(self, event): """Handle plug-in close.""" self.closingPlugin.emit() event.accept()
def index(self, row, column, parent): new_idx = QSortFilterProxyModel.index(self, row, self.LAYER_COL, parent) if column == self.LAYER_COL: return new_idx idx = self.createIndex(row, column, new_idx.internalId()) return idx
def refresh_model(self, source_model, db_connector=None, silent=False): filtered_source_model = QSortFilterProxyModel() filtered_source_model.setSourceModel(source_model) filtered_source_model.setFilterRole(int(SourceModel.Roles.TYPE)) self.print_info.emit(self.tr("Refresh available models:")) self.clear() previously_checked_models = self._checked_models self._checked_models = {} # models from db db_modelnames = self._db_modelnames(db_connector) # models from the repos filtered_source_model.setFilterFixedString("model") for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) modelname = filtered_source_model_index.data( int(SourceModel.Roles.NAME)) if modelname: enabled = modelname not in db_modelnames self.add_source( modelname, filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( modelname, filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked, ) if enabled and modelname not in self.checked_models() else Qt.Unchecked, enabled, ) if not silent: self.print_info.emit( self.tr("- Append (repository) model {}{}").format( modelname, " (inactive because it already exists in the database)" if not enabled else "", )) # models from the files filtered_source_model.setFilterFixedString("ili") for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) ili_file_path = filtered_source_model_index.data( int(SourceModel.Roles.PATH)) self.ilicache = IliCache(None, ili_file_path) models = self.ilicache.process_ili_file(ili_file_path) for model in models: if model["name"]: enabled = model["name"] not in db_modelnames self.add_source( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked if model is models[-1] and enabled and model["name"] not in self.checked_models() else Qt.Unchecked, ), enabled, ) if not silent: self.print_info.emit( self.tr("- Append (file) model {}{} from {}"). format( model["name"], " (inactive because it already exists in the database)" if not enabled else "", ili_file_path, )) # models from the transfer files filtered_source_model.setFilterRegExp("|".join(TransferExtensions)) for r in range(0, filtered_source_model.rowCount()): filtered_source_model_index = filtered_source_model.index( r, SourceModel.Columns.SOURCE) xtf_file_path = filtered_source_model_index.data( int(SourceModel.Roles.PATH)) models = self._transfer_file_models(xtf_file_path) for model in models: if model["name"]: enabled = model["name"] not in db_modelnames self.add_source( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.TYPE)), filtered_source_model_index.data( int(SourceModel.Roles.PATH)), filtered_source_model_index.data( int(SourceModel.Roles.ORIGIN_INFO)), previously_checked_models.get( ( model["name"], filtered_source_model_index.data( int(SourceModel.Roles.PATH)), ), Qt.Checked if enabled and model["name"] not in self.checked_models() else Qt.Unchecked, ), enabled, ) if not silent: self.print_info.emit( self.tr("- Append (xtf file) model {}{} from {}"). format( model["name"], " (inactive because it already exists in the database)" if not enabled else "", xtf_file_path, )) return self.rowCount()
def setup_class_table(self, classes=[]): # Load the codes each class will be recoded to. # # The "classes" parameter will include any mappings derived from a # class definition file, or, in the case or reading in user land cover # files, classes from the file itself. # # The default codes stored in self.default_classes are derived either # from the data/land_cover_classes_ESA_to_IPCC.json file when this class is # instantiated from the LCSetupWidget, or from the values within a # custom user data file when this class is instantiated from the # DlgDataIOImportLC class. if len(classes) > 0: input_codes = sorted([c['Initial_Code'] for c in classes]) default_codes = sorted([c['Initial_Code'] for c in self.default_classes]) new_codes = [c for c in input_codes if c not in default_codes] missing_codes = [c for c in default_codes if c not in input_codes] if len(new_codes) > 0: QtWidgets.QMessageBox.warning(None, self.tr("Warning"), self.tr(u"Some of the class codes ({}) in the definition file do not appear in the chosen data file.".format(', '.join([str(c) for c in new_codes])))) if len(missing_codes) > 0: QtWidgets.QMessageBox.warning(None, self.tr("Warning"), self.tr(u"Some of the class codes ({}) in the data file do not appear in the chosen definition file.".format(', '.join([str(c) for c in missing_codes])))) # Setup a new classes list with the new class codes for all classes # included in default classes, and any other class codes that are # missing added from the default class list classes = [c for c in classes if c['Initial_Code'] in default_codes] classes.extend([c for c in self.default_classes if c['Initial_Code'] not in input_codes]) else: classes = self.default_classes table_model = LCAggTableModel(classes, parent=self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.remap_view.setModel(proxy_model) # Add selector in cell for row in range(0, len(classes)): lc_class_combo = LCClassComboBox() # Now Set the default final codes for each row. Note that the # QComboBox entries are potentially translated, so need to link the # translated names back to a particular code. # Get the input code for this row and the final label it should map # to by default input_code = table_model.index(row, 0).data() final_label = [c['Final_Label'] for c in classes if c['Initial_Code'] == input_code][0] # Figure out which label translation this Final_Label (in English) # is equivalent to label_to_label_tr = {final_classes[key]['label']: key for key in final_classes.keys()} final_label_tr = label_to_label_tr[final_label] # Now find the index in the combo box of this translated final # label ind = lc_class_combo.findText(final_label_tr) if ind != -1: lc_class_combo.setCurrentIndex(ind) self.remap_view.setIndexWidget(proxy_model.index(row, 2), lc_class_combo) self.remap_view.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents) self.remap_view.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) self.remap_view.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) self.remap_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) return True
class DlgDownload(DlgCalculateBase, Ui_DlgDownload): def __init__(self, parent=None): """Constructor.""" super(DlgDownload, self).__init__(parent) # Allow the download tool to support data downloads of any size (in # terms of area) self._max_area = 1e10 self.settings = QSettings() self.setupUi(self) with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'gee_datasets.json')) as f: data_dict = json.load(f) self.datasets = [] for cat in list(data_dict.keys()): for title in list(data_dict[cat].keys()): item = data_dict[cat][title] item.update({'category': cat, 'title': title}) min_x = item.get('Min Longitude', None) max_x = item.get('Max Longitude', None) min_y = item.get('Min Latitude', None) max_y = item.get('Max Latitude', None) if not None in (min_x, max_x, min_y, max_y): extent_lat = '{} - {}'.format(min_y, max_y) extent_lon = '{} - {}'.format(min_x, max_x) item.update({'extent_lat': extent_lat, 'extent_lon': extent_lon}) self.datasets.append(item) self.update_data_table() self.data_view.selectionModel().selectionChanged.connect(self.selection_changed) self.data_view.viewport().installEventFilter(tool_tipper(self.data_view)) def selection_changed(self): if self.data_view.selectedIndexes(): # Note there can only be one row selected at a time by default row = list(set(index.row() for index in self.data_view.selectedIndexes()))[0] first_year = self.datasets[row]['Start year'] last_year = self.datasets[row]['End year'] if (first_year == 'NA') or (last_year == 'NA'): self.first_year.setEnabled(False) self.last_year.setEnabled(False) else: self.first_year.setEnabled(True) self.last_year.setEnabled(True) first_year = QDate(first_year, 12, 31) last_year = QDate(last_year, 12, 31) self.first_year.setMinimumDate(first_year) self.first_year.setMaximumDate(last_year) self.last_year.setMinimumDate(first_year) self.last_year.setMaximumDate(last_year) def tab_changed(self): super(DlgDownload, self).tab_changed() if (self.TabBox.currentIndex() == (self.TabBox.count() - 1)) \ and not self.data_view.selectedIndexes(): # Only enable download if a dataset is selected self.button_calculate.setEnabled(False) def firstShow(self): super(DlgDownload, self).firstShow() # Don't show the time selector for now self.TabBox.removeTab(1) def showEvent(self, event): super(DlgDownload, self).showEvent(event) # Don't local/cloud selector for this dialog self.options_tab.toggle_show_where_to_run(False) def update_data_table(self): table_model = DataTableModel(self.datasets, self) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(table_model) self.data_view.setModel(self.proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.datasets)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.data_view.setIndexWidget(self.proxy_model.index(row, 8), btn) self.data_view.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) self.data_view.horizontalHeader().setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(6, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(7, QtWidgets.QHeaderView.ResizeToContents) self.data_view.horizontalHeader().setSectionResizeMode(8, QtWidgets.QHeaderView.ResizeToContents) self.data_view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) def btn_details(self): button = self.sender() index = self.data_view.indexAt(button.pos()) #TODO: Code the details view def btn_calculate(self): # Note that the super class has several tests in it - if they fail it # returns False, which would mean this function should stop execution # as well. ret = super(DlgDownload, self).btn_calculate() if not ret: return rows = list(set(index.row() for index in self.data_view.selectedIndexes())) # Construct unique dataset names as the concatenation of the category # and the title selected_names = [self.proxy_model.index(row, 0).data() + self.proxy_model.index(row, 1).data()for row in rows] selected_datasets = [d for d in self.datasets if d['category'] + d['title'] in selected_names] self.close() crosses_180th, geojsons = self.gee_bounding_box for dataset in selected_datasets: payload = {'geojsons': json.dumps(geojsons), 'crs': self.aoi.get_crs_dst_wkt(), 'year_start': self.first_year.date().year(), 'year_end': self.last_year.date().year(), 'crosses_180th': crosses_180th, 'asset': dataset['GEE Dataset'], 'name': dataset['title'], 'temporal_resolution': dataset['Temporal resolution'], 'task_name': self.options_tab.task_name.text(), 'task_notes': self.options_tab.task_notes.toPlainText()} resp = run_script(get_script_slug('download-data'), payload) if resp: mb.pushMessage(self.tr("Success"), self.tr("Download request submitted to Google Earth Engine."), level=0, duration=5) else: mb.pushMessage(self.tr("Error"), self.tr("Unable to submit download request to Google Earth Engine."), level=0, duration=5)
class DlgJobs(QtWidgets.QDialog, Ui_DlgJobs): # When a connection to the api starts, emit true. When it ends, emit False connectionEvent = pyqtSignal(bool) def __init__(self, parent=None): """Constructor.""" super(DlgJobs, self).__init__(parent) self.setupUi(self) self.connection_in_progress = False self.bar = QgsMessageBar() self.bar.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.refresh.clicked.connect(self.btn_refresh) self.download.clicked.connect(self.btn_download) self.connectionEvent.connect(self.connection_event_changed) # Only enable download button if a job is selected self.download.setEnabled(False) self.jobs_view.viewport().installEventFilter( tool_tipper(self.jobs_view)) def showEvent(self, event): super(DlgJobs, self).showEvent(event) ####################################################################### ####################################################################### # Hack to download multiple countries at once for workshop preparation ####################################################################### ####################################################################### # from qgis.PyQt.QtCore import QTimer, Qt # from qgis.PyQt.QtWidgets import QMessageBox, QApplication # from qgis.PyQt.QtTest import QTest # from time import sleep # # self.btn_refresh() # # search_string = '_TE_Land_Productivity' # # # Ensure any message boxes that open are closed within 1 second # def close_msg_boxes(): # for w in QApplication.topLevelWidgets(): # if isinstance(w, QMessageBox): # print('Closing message box') # QTest.keyClick(w, Qt.Key_Enter) # timer = QTimer() # timer.timeout.connect(close_msg_boxes) # timer.start(1000) # jobs = [job for job in self.jobs if job['status'] == 'FINISHED' and \ # job['results']['type'] == 'CloudResults' and \ # re.search(search_string, job['task_name'])] # for job in jobs: # name = job['task_name'] # country = name.replace(search_string, '') # out_file = os.path.join(u'H:/Data/Trends.Earth/USB Stick Data/Trends.Earth_Data', country, u'{}.json'.format(name)) # #out_file = os.path.join(u'C:/Users/azvol/Desktop/TE_Indicators_For_USB', country, u'{}.json'.format(name)) # if not os.path.exists(out_file): # if not os.path.exists(os.path.dirname(out_file)): # os.makedirs(os.path.dirname(out_file)) # log(u'Downloading {} to {}'.format(name, out_file)) # download_cloud_results(job, # os.path.splitext(out_file)[0], # self.tr, # add_to_map=False) # sleep(2) ####################################################################### ####################################################################### # End hack ####################################################################### ####################################################################### try: jobs_cache = json.loads(QSettings().value("LDMP/jobs_cache", '{}')) except TypeError: # For backward compatibility need to handle case of jobs caches # that were stored inappropriately in past version of Trends.Earth jobs_cache = {} if jobs_cache is not {}: self.jobs = jobs_cache self.update_jobs_table() def connection_event_changed(self, flag): if flag: self.connection_in_progress = True self.download.setEnabled(False) self.refresh.setEnabled(False) else: self.connection_in_progress = False # Enable the download button if there is a selection self.selection_changed() self.refresh.setEnabled(True) def selection_changed(self): if self.connection_in_progress: return elif not self.jobs_view.selectedIndexes(): self.download.setEnabled(False) else: rows = set( list(index.row() for index in self.jobs_view.selectedIndexes())) job_ids = [self.proxy_model.index(row, 4).data() for row in rows] statuses = [ job.get('status', None) for job in self.jobs if job.get('id', None) in job_ids ] if len(statuses) > 0: for status in statuses: # Don't set button to enabled if any of the tasks aren't # yet finished, or if any are invalid if status != 'FINISHED': self.download.setEnabled(False) return self.download.setEnabled(True) def btn_refresh(self): self.connectionEvent.emit(True) email = get_user_email() if email: start_date = datetime.datetime.now() + datetime.timedelta(-14) jobs = get_execution(date=start_date.strftime('%Y-%m-%d')) if jobs: self.jobs = jobs # Add script names and descriptions to jobs list for job in self.jobs: # self.jobs will have prettified data for usage in table, # so save a backup of the original data under key 'raw' job['raw'] = job.copy() script = job.get('script_id', None) if script: job['script_name'] = job['script']['name'] # Clean up the script name so the version tag doesn't # look so odd job['script_name'] = re.sub(r'([0-9]+(_[0-9]+)+)$', r'(v\g<1>)', job['script_name']) job['script_name'] = job['script_name'].replace( '_', '.') job['script_description'] = job['script'][ 'description'] else: # Handle case of scripts that have been removed or that are # no longer supported job['script_name'] = self.tr('Script not found') job['script_description'] = self.tr('Script not found') # Pretty print dates and pull the metadata sent as input params for job in self.jobs: job['start_date'] = datetime.datetime.strftime( job['start_date'], '%Y/%m/%d (%H:%M)') job['end_date'] = datetime.datetime.strftime( job['end_date'], '%Y/%m/%d (%H:%M)') job['task_name'] = job['params'].get('task_name', '') job['task_notes'] = job['params'].get('task_notes', '') job['params'] = job['params'] # Cache jobs for later reuse QSettings().setValue( "LDMP/jobs_cache", json.dumps(self.jobs, default=json_serial)) self.update_jobs_table() self.connectionEvent.emit(False) return True self.connectionEvent.emit(False) return False def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(table_model) self.jobs_view.setModel(self.proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.jobs_view.setIndexWidget(self.proxy_model.index(row, 6), btn) self.jobs_view.horizontalHeader().setSectionResizeMode( 0, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 1, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 2, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 3, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 4, QtWidgets.QHeaderView.Fixed) self.jobs_view.horizontalHeader().setSectionResizeMode( 5, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 6, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed) def btn_details(self): button = self.sender() index = self.jobs_view.indexAt(button.pos()) details_dlg = DlgJobsDetails(self) job = self.jobs[index.row()] details_dlg.task_name.setText(job.get('task_name', '')) details_dlg.task_status.setText(job.get('status', '')) details_dlg.comments.setText(job.get('task_notes', '')) details_dlg.input.setText( json.dumps(job.get('params', ''), indent=4, sort_keys=True)) details_dlg.output.setText( json.dumps(job.get('results', ''), indent=4, sort_keys=True)) details_dlg.show() details_dlg.exec_() def btn_download(self): # Use set below in case multiple cells within the same row are selected rows = set( list(index.row() for index in self.jobs_view.selectedIndexes())) job_ids = [self.proxy_model.index(row, 4).data() for row in rows] jobs = [job for job in self.jobs if job['id'] in job_ids] filenames = [] for job in jobs: # Check if we need a download filename - some tasks don't need to # save data, but if any of the chosen tasks do, then we need to # choose a folder. Right now only TimeSeriesTable doesn't need a # filename. if job['results'].get('type') != 'TimeSeriesTable': f = None while not f: # Setup a string to use in filename window if job['task_name']: job_info = u'{} ({})'.format(job['script_name'], job['task_name']) else: job_info = job['script_name'] f, _ = QtWidgets.QFileDialog.getSaveFileName( self, self.tr( u'Choose a filename. Downloading results of: {}'. format(job_info)), QSettings().value("LDMP/output_dir", None), self.tr('Base filename (*.json)')) # Strip the extension so that it is a basename f = os.path.splitext(f)[0] if f: if os.access(os.path.dirname(f), os.W_OK): QSettings().setValue("LDMP/output_dir", os.path.dirname(f)) log(u"Downloading results to {} with basename {}". format(os.path.dirname(f), os.path.basename(f))) else: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr(u"Cannot write to {}. Choose a different base filename." .format(f))) else: return False filenames.append(f) else: filenames.append(None) self.close() for row, f in zip(rows, filenames): job = self.jobs[row] log(u"Processing job {}.".format(job.get('id', None))) result_type = job['results'].get('type') if result_type == 'CloudResults': download_cloud_results(job, f, self.tr) elif result_type == 'TimeSeriesTable': download_timeseries(job, self.tr) else: raise ValueError( "Unrecognized result type in download results: {}".format( result_type))
class geopunt4QgisDataCatalog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "en") if not locale: locale = 'en' else: locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QTranslator() self.translator.load(localePath) QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4QgisDataCatalogDlg() self.ui.setupUi(self) # get settings self.s = QSettings() self.loadSettings() self.gh = geometryHelper(self.iface) # setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QPushButton("Sluiten"), QDialogButtonBox.RejectRole) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) # vars self.firstShow = True self.wms = None self.wfs = None self.dl = None self.zoek = '' self.bronnen = None self.model = QStandardItemModel(self) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel(self.proxyModel) self.completer = QCompleter(self) self.completerModel = QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer) self.completer.setModel(self.completerModel) # eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.DLbtn.clicked.connect(lambda: self.openUrl(self.dl)) self.ui.resultView.clicked.connect(self.resultViewClicked) self.ui.modelFilterCbx.currentIndexChanged.connect(self.modelFilterCbxIndexChanged) self.ui.filterWgt.setHidden(1) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean) def loadSettings(self): self.timeout = int(self.s.value("geopunt4qgis/timeout", 15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.md = MDReader(self.timeout, self.proxy) def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/catalogus") def _setModel(self, records): self.model.clear() records = sorted(records, key=lambda k: k['title']) for rec in records: title = QStandardItem(rec['title']) # 0 wms = QStandardItem(rec['wms']) # 1 downloadLink = QStandardItem(rec['download']) # 2 id = QStandardItem(rec['uuid']) # 3 abstract = QStandardItem(rec['abstract']) # 4 wfs = QStandardItem(rec['wfs']) # 5 self.model.appendRow([title, wms, downloadLink, id, abstract, wfs]) # overwrite def show(self): QDialog.show(self) self.setWindowModality(0) metadataUrl = "https://metadata.geopunt.be" inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout, testSite= metadataUrl) if not inet: msg = "Kan geen verbing maken met de metadata van Geopunt: {} \nMogelijke is deze site niet bereikbaar, dan zal deze tool ook niet werken.\nProbeer later opnieuw. Indien dit probleem zich blijft voordoen contacteer informatie Vlaanderen.".format(metadataUrl) QMessageBox.warning(self.iface.mainWindow(), "Waarschuwing: kan geen verbinding maken", msg) self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), msg, level=Qgis.Warning, duration=3) return if self.firstShow: self.ui.GDIThemaCbx.addItems([''] + self.md.list_GDI_theme()) self.ui.organisatiesCbx.addItems([''] + self.md.list_organisations()) keywords = sorted(self.md.list_suggestionKeyword()) self.completerModel.setStringList(keywords) self.bronnen = self.md.list_bronnen() self.ui.bronCbx.addItems([''] + [n[1] for n in self.bronnen]) self.ui.typeCbx.addItems([''] + [n[0] for n in self.md.dataTypes]) self.ui.INSPIREannexCbx.addItems([''] + self.md.inspireannex) self.ui.INSPIREserviceCbx.addItems([''] + self.md.inspireServiceTypes) self.ui.INSPIREthemaCbx.addItems([''] + self.md.list_inspire_theme()) self.firstShow = False # eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() title = self.proxyModel.data(self.proxyModel.index(row, 0)) self.wms = self.proxyModel.data(self.proxyModel.index(row, 1)) self.dl = self.proxyModel.data(self.proxyModel.index(row, 2)) self.wfs = self.proxyModel.data(self.proxyModel.index(row, 5)) uuid = self.proxyModel.data(self.proxyModel.index(row, 3)) abstract = self.proxyModel.data(self.proxyModel.index(row, 4)) self.ui.descriptionText.setText( """<h3>%s</h3><div>%s</div><br/><div> <a href='https://metadata.geopunt.be/zoekdienst/apps/tabsearch/index.html?uuid=%s'> Ga naar fiche</a></div>""" % (title, abstract, uuid)) if self.wms: self.ui.addWMSbtn.setEnabled(1) else: self.ui.addWMSbtn.setEnabled(0) if self.wfs: self.ui.addWFSbtn.setEnabled(1) else: self.ui.addWFSbtn.setEnabled(0) if self.dl: self.ui.DLbtn.setEnabled(1) else: self.ui.DLbtn.setEnabled(0) def onZoekClicked(self): self.zoek = self.ui.zoekTxt.currentText() self.search() def modelFilterCbxIndexChanged(self): value = self.ui.modelFilterCbx.currentIndex() if value == 1: self.filterModel(1) elif value == 2: self.filterModel(5) elif value == 3: self.filterModel(2) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QRegExp("?*", Qt.CaseInsensitive, QRegExp.Wildcard) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: if self.ui.filterBox.isChecked(): themekey = self.ui.GDIThemaCbx.currentText() orgName = self.ui.organisatiesCbx.currentText() dataTypes = [n[1] for n in self.md.dataTypes if n[0] == self.ui.typeCbx.currentText()] if dataTypes != []: dataType = dataTypes[0] else: dataType = '' siteIds = [n[0] for n in self.bronnen if n[1] == self.ui.bronCbx.currentText()] if siteIds != []: siteId = siteIds[0] else: siteId = '' inspiretheme = self.ui.INSPIREthemaCbx.currentText() inspireannex = self.ui.INSPIREannexCbx.currentText() inspireServiceType = self.ui.INSPIREserviceCbx.currentText() searchResult = MDdata(self.md.searchAll( self.zoek, themekey, orgName, dataType, siteId, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = MDdata(self.md.searchAll(self.zoek)) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=3) return self.ui.countLbl.setText("Aantal gevonden: %s" % searchResult.count) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QCoreApplication.translate("geopunt4QgisPoidialog", "Er werden geen resultaten gevonde voor deze zoekopdracht"), duration=5) def openUrl(self, url): if url: webbrowser.open_new_tab(url.encode("utf-8")) def addWMS(self): if self.wms == None: return crs = self.gh.getGetMapCrs(self.iface).authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' try: lyrs = getWmsLayerNames(self.wms, self.proxy) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wms), level=Qgis.Warning, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QInputDialog.getItem(self, "WMS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle][0] style = [n[2] for n in lyrs if n[1] == layerTitle][0] if not style: style = "" url = self.wms.split('?')[0] if crs != 'EPSG:31370' or crs != 'EPSG:3857': crs = 'EPSG:31370' wmsUrl = "contextualWMSLegend=0&dpiMode=7&url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % ( url, layerName, style, crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsProject.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan WMS niet laden"), level=Qgis.Critical, duration=10) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = getWFSLayerNames(self.wfs, self.proxy) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wfs), level=Qgis.Warning, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QInputDialog.getItem(self, "WFS toevoegen", "Kies een laag om toe te voegen", [n[1] for n in lyrs], editable=0) if not accept: return layerName = [n[0] for n in lyrs if n[1] == layerTitle][0] crs = [n[2] for n in lyrs if n[1] == layerTitle][0] url = self.wfs.split('?')[0] wfsUri = makeWFSuri(url, layerName, crs ) try: vlayer = QgsVectorLayer(wfsUri, layerTitle, "WFS") QgsProject.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=Qgis.Critical, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.ui.zoekTxt.setCurrentIndex(0) self.ui.descriptionText.setText('') self.ui.countLbl.setText("") self.ui.msgLbl.setText("") self.ui.DLbtn.setEnabled(0) self.ui.addWFSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0) self.ui.modelFilterCbx.setCurrentIndex(0)