def __init__(self, iface) -> None: super().__init__() self.options = get_options() self.msg_bar = MessageBar(iface) self.tree_widget = QTreeWidget() self.populate_tree() self.select_mandatory_hires_button = QPushButton( 'Select Mandatory Fields in Highest Resolution') self.select_mandatory_hires_button.clicked.connect( self.on_select_mandatory_hires_button_clicked) self.select_mandatory_lores_button = QPushButton( 'Select Mandatory Fields in Lowest Resolution') self.select_mandatory_lores_button.clicked.connect( self.on_select_mandatory_lores_button_clicked) self.download_button = QPushButton('Download Selected Datasets') self.download_button.clicked.connect(self.on_download_button_clicked) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, PROGRESS_BAR_MAX) self.progress_bar.setTextVisible(False) self.progress_bar.hide() vbox = QVBoxLayout() vbox.addWidget(self.tree_widget) vbox.addWidget(self.select_mandatory_hires_button) vbox.addWidget(self.select_mandatory_lores_button) vbox.addWidget(self.download_button) vbox.addWidget(self.progress_bar) self.setLayout(vbox)
def __init__(self, iface: QgisInterface) -> None: super().__init__() self.iface = iface self.options = get_options() self.msg_bar = MessageBar(iface) self.wps_box, [open_namelist_wps, prepare_only_wps, run_geogrid, run_ungrib, run_metgrid, open_output_wps] = \ self.create_gbox_with_btns('WPS', [ 'Open Configuration', 'Prepare only', ['Run Geogrid', 'Run Ungrib', 'Run Metgrid'], 'Visualize Output' ]) self.wrf_box, [open_namelist_wrf, prepare_only_wrf, run_real, run_wrf, open_output_wrf] = \ self.create_gbox_with_btns('WRF', [ 'Open Configuration', 'Prepare only', ['Run Real', 'Run WRF'], 'Visualize Output' ]) self.control_box, [kill_program] = self.create_gbox_with_btns( 'Program control', ['Kill Program']) self.control_box.setVisible(False) self.stdout_box, self.stdout_textarea, self.stdout_highlighter = \ self.create_stdout_box() vbox = QVBoxLayout() vbox.addWidget(self.wps_box) vbox.addWidget(self.wrf_box) vbox.addWidget(self.control_box) vbox.addWidget(self.stdout_box) self.stdout_box.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.setLayout(vbox) open_namelist_wps.clicked.connect(self.on_open_namelist_wps_clicked) prepare_only_wps.clicked.connect(self.on_prepare_only_wps_clicked) run_geogrid.clicked.connect(self.on_run_geogrid_clicked) run_ungrib.clicked.connect(self.on_run_ungrib_clicked) run_metgrid.clicked.connect(self.on_run_metgrid_clicked) open_output_wps.clicked.connect(self.on_open_output_wps_clicked) open_namelist_wrf.clicked.connect(self.on_open_namelist_wrf_clicked) prepare_only_wrf.clicked.connect(self.on_prepare_only_wrf_clicked) run_real.clicked.connect(self.on_run_real_clicked) run_wrf.clicked.connect(self.on_run_wrf_clicked) open_output_wrf.clicked.connect(self.on_open_output_wrf_clicked) kill_program.clicked.connect(self.on_kill_program_clicked)
class GeoToolsDownloadManager(QWidget): def __init__(self, iface) -> None: super().__init__() self.options = get_options() self.msg_bar = MessageBar(iface) self.tree_widget = QTreeWidget() self.populate_tree() self.select_mandatory_hires_button = QPushButton( 'Select Mandatory Fields in Highest Resolution') self.select_mandatory_hires_button.clicked.connect( self.on_select_mandatory_hires_button_clicked) self.select_mandatory_lores_button = QPushButton( 'Select Mandatory Fields in Lowest Resolution') self.select_mandatory_lores_button.clicked.connect( self.on_select_mandatory_lores_button_clicked) self.download_button = QPushButton('Download Selected Datasets') self.download_button.clicked.connect(self.on_download_button_clicked) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, PROGRESS_BAR_MAX) self.progress_bar.setTextVisible(False) self.progress_bar.hide() vbox = QVBoxLayout() vbox.addWidget(self.tree_widget) vbox.addWidget(self.select_mandatory_hires_button) vbox.addWidget(self.select_mandatory_lores_button) vbox.addWidget(self.download_button) vbox.addWidget(self.progress_bar) self.setLayout(vbox) def populate_tree(self) -> None: self.tree_widget.setHeaderLabels(['ID', 'Resolution', 'Description']) self.tree_widget.setRootIsDecorated(False) self.tree_widget.setSortingEnabled(True) self.tree_widget.sortByColumn(0, Qt.AscendingOrder) header = self.tree_widget.header() header.setSectionsMovable(False) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.Stretch) for dataset_id, (description, resolution) in geo_datasets.items(): item = QTreeWidgetItem(self.tree_widget) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setText(0, dataset_id) item.setData(0, Qt.UserRole, dataset_id) item.setCheckState(0, Qt.Unchecked) if is_geo_dataset_downloaded(dataset_id, self.options.geog_dir): item.setFlags(Qt.NoItemFlags) item.setToolTip( 0, 'Dataset downloaded in: {}'.format( get_geo_dataset_path(dataset_id, self.options.geog_dir))) else: item.setToolTip(0, dataset_id) if isinstance(resolution, str): item.setText(1, resolution) else: item.setText(1, formatted_dd_to_dms(resolution)) item.setToolTip(1, '{}°'.format(resolution)) item.setText(2, description) item.setToolTip(2, description) def on_select_mandatory_lores_button_clicked(self): self.select_datasets(geo_datasets_mandatory_lores) def on_select_mandatory_hires_button_clicked(self): self.select_datasets(geo_datasets_mandatory_hires) def select_datasets(self, names: List[str]) -> None: items = self.get_items() for name, item in items.items(): item.setCheckState(0, Qt.Checked if name in names else Qt.Unchecked) def get_items(self) -> Dict[str, QTreeWidgetItem]: items = {} # type: Dict[str,QTreeWidgetItem] for index in range(self.tree_widget.topLevelItemCount()): item = self.tree_widget.topLevelItem(index) items[item.data(0, Qt.UserRole)] = item return items def on_download_button_clicked(self) -> None: datasets_to_download = [] for name, item in self.get_items().items(): if item.checkState(0) == Qt.Checked: datasets_to_download.append(name) thread = TaskThread( lambda: self.download_datasets(datasets_to_download), yields_progress=True) thread.started.connect(self.on_started_download) thread.progress.connect(self.on_progress_download) thread.finished.connect(self.on_finished_download) thread.succeeded.connect(self.on_successful_download) thread.failed.connect(reraise) thread.start() def on_started_download(self): self.download_button.hide() self.progress_bar.show() self.tree_widget.setEnabled(False) def on_progress_download(self, progress: float, status: str) -> None: bar_value = int(progress * PROGRESS_BAR_MAX) self.progress_bar.setValue(bar_value) self.progress_bar.repaint() # otherwise just updates in 1% steps logger.debug(f'Geo data download: {progress*100:.1f}% - {status}') def on_finished_download(self) -> None: self.download_button.show() self.progress_bar.hide() self.tree_widget.setEnabled(True) self.tree_widget.clear() self.populate_tree() Broadcast.geo_datasets_updated.emit() def on_successful_download(self) -> None: self.msg_bar.success('Geographical datasets downloaded successfully.') def download_datasets( self, dataset_names: List[str]) -> Iterable[Tuple[float, str]]: yield 0.0, 'selected: ' + ', '.join(dataset_names) for i, name in enumerate(dataset_names): for dataset_progress in download_and_extract_geo_dataset( name, self.options.geog_dir): total_progress = (i + dataset_progress) / len(dataset_names) yield total_progress, f'downloading {name} ({dataset_progress*100:.1f}%)'
class RunWidget(QWidget): tab_active = pyqtSignal() view_wrf_nc_file = pyqtSignal(str) def __init__(self, iface: QgisInterface) -> None: super().__init__() self.iface = iface self.options = get_options() self.msg_bar = MessageBar(iface) self.wps_box, [open_namelist_wps, prepare_only_wps, run_geogrid, run_ungrib, run_metgrid, open_output_wps] = \ self.create_gbox_with_btns('WPS', [ 'Open Configuration', 'Prepare only', ['Run Geogrid', 'Run Ungrib', 'Run Metgrid'], 'Visualize Output' ]) self.wrf_box, [open_namelist_wrf, prepare_only_wrf, run_real, run_wrf, open_output_wrf] = \ self.create_gbox_with_btns('WRF', [ 'Open Configuration', 'Prepare only', ['Run Real', 'Run WRF'], 'Visualize Output' ]) self.control_box, [kill_program] = self.create_gbox_with_btns( 'Program control', ['Kill Program']) self.control_box.setVisible(False) self.stdout_box, self.stdout_textarea, self.stdout_highlighter = \ self.create_stdout_box() vbox = QVBoxLayout() vbox.addWidget(self.wps_box) vbox.addWidget(self.wrf_box) vbox.addWidget(self.control_box) vbox.addWidget(self.stdout_box) self.stdout_box.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.setLayout(vbox) open_namelist_wps.clicked.connect(self.on_open_namelist_wps_clicked) prepare_only_wps.clicked.connect(self.on_prepare_only_wps_clicked) run_geogrid.clicked.connect(self.on_run_geogrid_clicked) run_ungrib.clicked.connect(self.on_run_ungrib_clicked) run_metgrid.clicked.connect(self.on_run_metgrid_clicked) open_output_wps.clicked.connect(self.on_open_output_wps_clicked) open_namelist_wrf.clicked.connect(self.on_open_namelist_wrf_clicked) prepare_only_wrf.clicked.connect(self.on_prepare_only_wrf_clicked) run_real.clicked.connect(self.on_run_real_clicked) run_wrf.clicked.connect(self.on_run_wrf_clicked) open_output_wrf.clicked.connect(self.on_open_output_wrf_clicked) kill_program.clicked.connect(self.on_kill_program_clicked) @property def project(self) -> Project: return self._project @project.setter def project(self, val: Project) -> None: ''' Sets the currently active project. See tab_simulation. ''' self._project = val def prepare_wps_run(self) -> None: if not self.options.wps_dir: raise RuntimeError('WPS is not available!') self.project.prepare_wps_run(self.options.wps_dir) def prepare_wrf_run(self) -> None: if not self.options.wrf_dir: raise RuntimeError('WRF is not available!') self.project.prepare_wrf_run(self.options.wrf_dir) def on_open_namelist_wps_clicked(self) -> None: self.project.update_wps_namelist() self.open_editor_dialog(self.project.wps_namelist_path, get_namelist_schema('wps')) def on_open_namelist_wrf_clicked(self) -> None: # TODO wrap exceptions (from wrf_namelist.py) into message bar messages # (same for prepare_*_run) self.project.update_wrf_namelist() self.open_editor_dialog(self.project.wrf_namelist_path, get_namelist_schema('wrf')) def on_prepare_only_wps_clicked(self) -> None: self.prepare_wps_run() self.msg_bar.success('Successfully prepared WPS files in ' + self.project.run_wps_folder) def on_prepare_only_wrf_clicked(self) -> None: self.prepare_wrf_run() self.msg_bar.success('Successfully prepared WRF files in ' + self.project.run_wrf_folder) def on_run_geogrid_clicked(self) -> None: self.prepare_wps_run() # TODO move this into core self.run_program(self.options.geogrid_exe, self.project.run_wps_folder, supports_mpi=True) def on_run_ungrib_clicked(self) -> None: self.prepare_wps_run() self.run_program(self.options.ungrib_exe, self.project.run_wps_folder, supports_mpi=False) def on_run_metgrid_clicked(self) -> None: self.prepare_wps_run() self.run_program(self.options.metgrid_exe, self.project.run_wps_folder, supports_mpi=True) def on_run_real_clicked(self) -> None: self.prepare_wrf_run() self.run_program(self.options.real_exe, self.project.run_wrf_folder, supports_mpi=True) def on_run_wrf_clicked(self) -> None: self.prepare_wrf_run() self.run_program(self.options.wrf_exe, self.project.run_wrf_folder, supports_mpi=True) def on_open_output_wps_clicked(self) -> None: path, _ = QFileDialog.getOpenFileName( caption='Open WRF NetCDF File', directory=self.project.run_wps_folder, filter='geo_em*;met_em*') if not path: return self.view_wrf_nc_file.emit(path) def on_open_output_wrf_clicked(self) -> None: path, _ = QFileDialog.getOpenFileName( caption='Open WRF NetCDF File', directory=self.project.run_wrf_folder, filter='wrfinput*;wrfbdy*;wrfout*') if not path: return self.view_wrf_nc_file.emit(path) def on_kill_program_clicked(self) -> None: self.dont_report_program_status = True self.thread.kill_program() def run_program(self, path: str, cwd: str, supports_mpi: bool) -> None: self.dont_report_program_status = False self.wps_box.setVisible(False) self.wrf_box.setVisible(False) self.control_box.setVisible(True) self.run_program_in_background(path, cwd, self.on_program_execution_done, supports_mpi) def on_program_execution_done(self, path: str, error: Union[bool, str, None]) -> None: self.wps_box.setVisible(True) self.wrf_box.setVisible(True) self.control_box.setVisible(False) # The above causes a resize of the program output textarea # which requires that we scroll to the bottom again. vert_scrollbar = self.stdout_textarea.verticalScrollBar() vert_scrollbar.setValue(vert_scrollbar.maximum()) if self.dont_report_program_status: return if error is None: # An exception that will be reported by the caller after this function returns. return if error: if isinstance(error, str): QMessageBox.critical( self.iface.mainWindow(), PLUGIN_NAME, 'Program {} failed: {}'.format(os.path.basename(path), error)) else: QMessageBox.critical( self.iface.mainWindow(), PLUGIN_NAME, 'Program {} failed with errors, check the logs.'.format( os.path.basename(path))) else: QMessageBox.information( self.iface.mainWindow(), PLUGIN_NAME, 'Program {} finished without errors!'.format( os.path.basename(path))) def open_editor_dialog(self, path: str, nml_schema: dict) -> Optional[str]: dialog = NmlEditorDialog(path, nml_schema) dialog.exec_() def create_gbox_with_btns(self, gbox_name: str, btn_names: List[Union[str,List[str]]]) \ -> Tuple[QGroupBox, List[QPushButton]]: vbox = QVBoxLayout() btns = [] for name_or_list in btn_names: if isinstance(name_or_list, str): name = name_or_list btn = QPushButton(name) btns.append(btn) vbox.addWidget(btn) else: hbox = QHBoxLayout() for name in name_or_list: btn = QPushButton(name) btns.append(btn) hbox.addWidget(btn) vbox.addLayout(hbox) gbox = QGroupBox(gbox_name) gbox.setLayout(vbox) return gbox, btns def create_stdout_box( self) -> Tuple[QGroupBox, QPlainTextEdit, QSyntaxHighlighter]: gbox = QGroupBox('Program output') vbox = QVBoxLayout() gbox.setLayout(vbox) text_area = QPlainTextEdit() vbox.addWidget(text_area) text_area.setReadOnly(True) text_area.setWordWrapMode(QTextOption.NoWrap) palette = text_area.palette() # type: QPalette palette.setColor(QPalette.Active, QPalette.Base, QColor('#1E1E1E')) palette.setColor(QPalette.Inactive, QPalette.Base, QColor('#1E1E1E')) text_area.setPalette(palette) font = QFont('Monospace', 9) font.setStyleHint(QFont.TypeWriter) fmt = QTextCharFormat() fmt.setFont(font) fmt.setForeground(QColor(212, 212, 212)) text_area.setCurrentCharFormat(fmt) doc = text_area.document() highlighter = LogSeverityHighlighter(doc) return gbox, text_area, highlighter def run_program_in_background(self, path: str, cwd: str, on_done: Callable[ [str, Union[bool, str, None]], None], supports_mpi: bool) -> None: self.stdout_textarea.clear() # WRF/WPS does not use exit codes to indicate success/failure, # therefore in addition we look for a pattern in the program output. # TODO add 'FATAL' as patterm wrf_error_pattern = 'ERROR' # Using QThread and signals (instead of a plain Python thread) is necessary # so that the on_done callback is run on the UI thread, instead of the worker thread. thread = ProgramThread(path, cwd, wrf_error_pattern, use_mpi=supports_mpi and self.options.mpi_enabled, mpi_processes=self.options.mpi_processes) def on_output(out: str) -> None: self.stdout_textarea.appendPlainText(out) vert_scrollbar = self.stdout_textarea.verticalScrollBar() vert_scrollbar.setValue(vert_scrollbar.maximum()) def on_finished() -> None: # When lines come in fast, then the highlighter is not called on each line. # Re-highlighting at the end is a work-around to at least have correct # highlighting after program termination. self.stdout_highlighter.rehighlight() if thread.exc_info: on_done(path, None) raise thread.exc_info[0].with_traceback(*thread.exc_info[1:]) on_done(path, thread.error) thread.output.connect(on_output) thread.finished.connect(on_finished) thread.start() # so that we can kill the program later if requested self.thread = thread
def __init__(self, iface) -> None: super().__init__() self.iface = iface self.options = get_options() self.msg_bar = MessageBar(iface) vbox = QVBoxLayout() self.setLayout(vbox) hbox = QHBoxLayout() vbox.addLayout(hbox) hbox.addWidget(QLabel('Dataset: ')) self.cbox_dataset = QComboBox() self.cbox_dataset.addItem('-') for index, (dataset_name, dataset_label) in enumerate(met_datasets.items()): self.cbox_dataset.addItem(dataset_name, dataset_name) self.cbox_dataset.setItemData(index + 1, dataset_label, Qt.ToolTipRole) self.cbox_dataset.currentIndexChanged.connect(self.on_dataset_changed) hbox.addWidget(self.cbox_dataset) hbox_product_name = QHBoxLayout() vbox.addLayout(hbox_product_name) hbox_product_name.addWidget(QLabel('Product: ')) self.cbox_product = QComboBox() self.cbox_product.currentIndexChanged.connect(self.on_product_changed) hbox_product_name.addWidget(self.cbox_product) hbox_start_datetime = QHBoxLayout() vbox.addLayout(hbox_start_datetime) self.dedit_start_date = QDateTimeEdit() self.dedit_start_date.setCalendarPopup(True) hbox_start_datetime.addWidget(QLabel('Start: ')) hbox_start_datetime.addWidget(self.dedit_start_date) hbox_end_datetime = QHBoxLayout() vbox.addLayout(hbox_end_datetime) self.dedit_end_date = QDateTimeEdit() self.dedit_end_date.setCalendarPopup(True) hbox_end_datetime.addWidget(QLabel('End: ')) hbox_end_datetime.addWidget(self.dedit_end_date) gbox_extent = QGroupBox('Extent') vbox.addWidget(gbox_extent) vbox_extent = QVBoxLayout() gbox_extent.setLayout(vbox_extent) hbox_extent = QHBoxLayout() vbox_extent.addLayout(hbox_extent) self.radio_global = QRadioButton('Global') self.radio_global.toggled.connect(self.on_extent_radio_button_clicked) hbox_extent.addWidget(self.radio_global) self.radio_subset = QRadioButton('Subset') self.radio_subset.toggled.connect(self.on_extent_radio_button_clicked) hbox_extent.addWidget(self.radio_subset) self.widget_extent = QWidget() vbox_extent.addWidget(self.widget_extent) grid_extent = QGridLayout() self.widget_extent.setLayout(grid_extent) self.widget_extent.hide() self.top = add_grid_lineedit(grid_extent, 0, 'North Latitude', LAT_VALIDATOR, '°', required=True) self.right = add_grid_lineedit(grid_extent, 1, 'East Longitude', LON_VALIDATOR, '°', required=True) self.left = add_grid_lineedit(grid_extent, 2, 'West Longitude', LON_VALIDATOR, '°', required=True) self.bottom = add_grid_lineedit(grid_extent, 3, 'South Latitude', LAT_VALIDATOR, '°', required=True) self.extent_from_active_layer = QPushButton('Set from Active Layer') grid_extent.addWidget(self.extent_from_active_layer, 4, 1) self.extent_from_active_layer.clicked.connect( self.on_extent_from_active_layer_button_clicked) self.radio_global.setChecked(True) self.tree = QListWidget() vbox_tree = QVBoxLayout() vbox.addLayout(vbox_tree) vbox_tree.addWidget(self.tree) self.btn_download = QPushButton('Download') self.btn_download.clicked.connect(self.on_download_button_clicked) vbox.addWidget(self.btn_download) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, PROGRESS_BAR_MAX) self.progress_bar.setTextVisible(False) self.progress_bar.hide() vbox.addWidget(self.progress_bar)
class MetToolsDownloadManager(QWidget): def __init__(self, iface) -> None: super().__init__() self.iface = iface self.options = get_options() self.msg_bar = MessageBar(iface) vbox = QVBoxLayout() self.setLayout(vbox) hbox = QHBoxLayout() vbox.addLayout(hbox) hbox.addWidget(QLabel('Dataset: ')) self.cbox_dataset = QComboBox() self.cbox_dataset.addItem('-') for index, (dataset_name, dataset_label) in enumerate(met_datasets.items()): self.cbox_dataset.addItem(dataset_name, dataset_name) self.cbox_dataset.setItemData(index + 1, dataset_label, Qt.ToolTipRole) self.cbox_dataset.currentIndexChanged.connect(self.on_dataset_changed) hbox.addWidget(self.cbox_dataset) hbox_product_name = QHBoxLayout() vbox.addLayout(hbox_product_name) hbox_product_name.addWidget(QLabel('Product: ')) self.cbox_product = QComboBox() self.cbox_product.currentIndexChanged.connect(self.on_product_changed) hbox_product_name.addWidget(self.cbox_product) hbox_start_datetime = QHBoxLayout() vbox.addLayout(hbox_start_datetime) self.dedit_start_date = QDateTimeEdit() self.dedit_start_date.setCalendarPopup(True) hbox_start_datetime.addWidget(QLabel('Start: ')) hbox_start_datetime.addWidget(self.dedit_start_date) hbox_end_datetime = QHBoxLayout() vbox.addLayout(hbox_end_datetime) self.dedit_end_date = QDateTimeEdit() self.dedit_end_date.setCalendarPopup(True) hbox_end_datetime.addWidget(QLabel('End: ')) hbox_end_datetime.addWidget(self.dedit_end_date) gbox_extent = QGroupBox('Extent') vbox.addWidget(gbox_extent) vbox_extent = QVBoxLayout() gbox_extent.setLayout(vbox_extent) hbox_extent = QHBoxLayout() vbox_extent.addLayout(hbox_extent) self.radio_global = QRadioButton('Global') self.radio_global.toggled.connect(self.on_extent_radio_button_clicked) hbox_extent.addWidget(self.radio_global) self.radio_subset = QRadioButton('Subset') self.radio_subset.toggled.connect(self.on_extent_radio_button_clicked) hbox_extent.addWidget(self.radio_subset) self.widget_extent = QWidget() vbox_extent.addWidget(self.widget_extent) grid_extent = QGridLayout() self.widget_extent.setLayout(grid_extent) self.widget_extent.hide() self.top = add_grid_lineedit(grid_extent, 0, 'North Latitude', LAT_VALIDATOR, '°', required=True) self.right = add_grid_lineedit(grid_extent, 1, 'East Longitude', LON_VALIDATOR, '°', required=True) self.left = add_grid_lineedit(grid_extent, 2, 'West Longitude', LON_VALIDATOR, '°', required=True) self.bottom = add_grid_lineedit(grid_extent, 3, 'South Latitude', LAT_VALIDATOR, '°', required=True) self.extent_from_active_layer = QPushButton('Set from Active Layer') grid_extent.addWidget(self.extent_from_active_layer, 4, 1) self.extent_from_active_layer.clicked.connect( self.on_extent_from_active_layer_button_clicked) self.radio_global.setChecked(True) self.tree = QListWidget() vbox_tree = QVBoxLayout() vbox.addLayout(vbox_tree) vbox_tree.addWidget(self.tree) self.btn_download = QPushButton('Download') self.btn_download.clicked.connect(self.on_download_button_clicked) vbox.addWidget(self.btn_download) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, PROGRESS_BAR_MAX) self.progress_bar.setTextVisible(False) self.progress_bar.hide() vbox.addWidget(self.progress_bar) def on_dataset_changed(self, index: int): self.cbox_product.clear() dataset_name = self.cbox_dataset.currentData() if dataset_name is None: return auth = (self.options.rda_username, self.options.rda_password) self.products = get_met_products(dataset_name, auth) for product in self.products.keys(): self.cbox_product.addItem(product, product) def on_product_changed(self, index: int): if index == -1: return self.tree.clear() product_name = self.cbox_product.currentData() current_avail_vars = self.products[product_name] dates = [] for name in current_avail_vars.keys(): item = QListWidgetItem(current_avail_vars[name]['label']) item.setData(Qt.UserRole, name) item.setCheckState(Qt.Checked) self.tree.addItem(item) dates.append(current_avail_vars[name]['start_date']) dates.append(current_avail_vars[name]['end_date']) date_min = min(dates) date_max = max(dates) for dt_input in [self.dedit_start_date, self.dedit_end_date]: dt_input.setDateTimeRange( QDateTime(QDate(date_min.year, date_min.month, date_min.day), QTime(date_min.hour, date_min.minute)), QDateTime(QDate(date_max.year, date_max.month, date_max.day), QTime(date_max.hour, date_max.minute))) min_dt = self.dedit_start_date.minimumDateTime() max_dt = self.dedit_start_date.maximumDateTime() self.dedit_start_date.setDateTime(min_dt) self.dedit_end_date.setDateTime(max_dt) def on_download_button_clicked(self): param_names = [] for index in range(self.tree.count()): item = self.tree.item(index) if item.checkState() == Qt.Checked: param_name = item.data(Qt.UserRole) param_names.append(param_name) dataset_name = self.cbox_dataset.currentData() product_name = self.cbox_product.currentData() start_date = self.dedit_start_date.dateTime().toPyDateTime() end_date = self.dedit_end_date.dateTime().toPyDateTime() if dataset_name is None or product_name is None: raise UserError('Dataset/Product not selected') args = [ self.options.met_dir, dataset_name, product_name, start_date, end_date ] if is_met_dataset_downloaded(*args): reply = QMessageBox.question(self.iface.mainWindow( ), 'Existing dataset', ( 'You already downloaded data with the selected dataset/product/date/time combination. ' 'If you continue, this data will be removed.\n' 'Location: {}'.format(get_met_dataset_path(*args))), QMessageBox.Ok, QMessageBox.Cancel) if reply == QMessageBox.Cancel: return lat_north = self.top.value() lat_south = self.bottom.value() lon_west = self.left.value() lon_east = self.right.value() auth = (self.options.rda_username, self.options.rda_password) thread = TaskThread(lambda: download_met_dataset( self.options.met_dir, auth, dataset_name, product_name, param_names, start_date, end_date, lat_south, lat_north, lon_west, lon_east), yields_progress=True) thread.started.connect(self.on_started_download) thread.progress.connect(self.on_progress_download) thread.finished.connect(self.on_finished_download) thread.succeeded.connect(self.on_successful_download) thread.failed.connect(reraise) thread.start() def on_started_download(self) -> None: self.btn_download.hide() self.progress_bar.show() def on_progress_download(self, progress: float, status: str) -> None: bar_value = int(progress * PROGRESS_BAR_MAX) self.progress_bar.setValue(bar_value) self.progress_bar.repaint() # otherwise just updates in 1% steps if status == 'submitted': self.msg_bar.info( 'Met dataset download request submitted successfully, waiting until available for download...' ) elif status == 'ready': self.msg_bar.info( 'Met dataset download request is now ready, downloading...') logger.debug(f'Met data download: {progress*100:.1f}% - {status}') def on_finished_download(self) -> None: self.btn_download.show() self.progress_bar.hide() def on_successful_download(self) -> None: self.msg_bar.success('Meteorological dataset downloaded successfully.') Broadcast.met_datasets_updated.emit() def on_extent_radio_button_clicked(self): if self.radio_global.isChecked(): self.top.set_value(90) self.bottom.set_value(-90) self.left.set_value(-180) self.right.set_value(180) self.top.setDisabled(True) self.bottom.setDisabled(True) self.left.setDisabled(True) self.right.setDisabled(True) self.widget_extent.hide() elif self.radio_subset.isChecked(): self.widget_extent.show() self.top.setDisabled(False) self.bottom.setDisabled(False) self.left.setDisabled(False) self.right.setDisabled(False) def on_extent_from_active_layer_button_clicked(self): layer = self.iface.activeLayer() # type: Optional[QgsMapLayer] if layer is None: return layer_crs = CRS(layer.crs().toProj4()) target_crs = CRS('+proj=latlong +datum=WGS84') extent = layer.extent() # type: QgsRectangle bbox = rect_to_bbox(extent) bbox_geo = layer_crs.transform_bbox(bbox, target_crs.srs) padding = 5 # degrees lat_south = max(bbox_geo.miny - 5, -90) lat_north = min(bbox_geo.maxy + 5, 90) lon_west = max(bbox_geo.minx - 5, -180) lon_east = min(bbox_geo.maxx + 5, 180) self.bottom.set_value(lat_south) self.top.set_value(lat_north) self.left.set_value(lon_west) self.right.set_value(lon_east)
class GeoToolsDownloadManager(QWidget): def __init__(self, iface) -> None: super().__init__() self.options = get_options() self.msg_bar = MessageBar(iface) self.tree_widget = QTreeWidget() self.populate_tree() self.select_mandatory_hires_button = QPushButton( 'Select Mandatory Fields in Highest Resolution') self.select_mandatory_hires_button.clicked.connect( self.on_select_mandatory_hires_button_clicked) self.select_mandatory_lores_button = QPushButton( 'Select Mandatory Fields in Lowest Resolution') self.select_mandatory_lores_button.clicked.connect( self.on_select_mandatory_lores_button_clicked) self.download_button = QPushButton('Download Selected Datasets') self.download_button.clicked.connect(self.on_download_button_clicked) self.progress_bar = QProgressBar() self.progress_bar.setRange(0, 0) self.progress_bar.setTextVisible(False) self.progress_bar.hide() vbox = QVBoxLayout() vbox.addWidget(self.tree_widget) vbox.addWidget(self.select_mandatory_hires_button) vbox.addWidget(self.select_mandatory_lores_button) vbox.addWidget(self.download_button) vbox.addWidget(self.progress_bar) self.setLayout(vbox) def populate_tree(self) -> None: self.tree_widget.setHeaderItem( QTreeWidgetItem(['ID', 'Description', 'Resolution'])) self.tree_widget.setRootIsDecorated(False) self.tree_widget.setSortingEnabled(True) for id, (description, resolution) in geo_datasets.items(): item = QTreeWidgetItem(self.tree_widget) item.setText(0, id) item.setData(0, Qt.UserRole, id) item.setCheckState(0, Qt.Unchecked) if is_geo_dataset_downloaded(id, self.options.geog_dir): item.setFlags(Qt.NoItemFlags) item.setToolTip( 0, 'Dataset downloaded in: {}'.format( get_geo_dataset_path(id, self.options.geog_dir))) else: item.setToolTip(0, id) item.setText(1, description) item.setToolTip(1, description) if isinstance(resolution, str): item.setText(2, resolution) else: item.setText(2, formatted_dd_to_dms(resolution)) item.setToolTip(2, '{}°'.format(resolution)) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) def on_select_mandatory_lores_button_clicked(self): self.select_datasets(geo_datasets_mandatory_lores) def on_select_mandatory_hires_button_clicked(self): self.select_datasets(geo_datasets_mandatory_hires) def select_datasets(self, names: List[str]) -> None: items = self.get_items() for name, item in items.items(): item.setCheckState(0, Qt.Checked if name in names else Qt.Unchecked) def get_items(self) -> Dict[str, QTreeWidgetItem]: items = {} # type: Dict[str,QTreeWidgetItem] for index in range(self.tree_widget.topLevelItemCount()): item = self.tree_widget.topLevelItem(index) items[item.data(0, Qt.UserRole)] = item return items def on_download_button_clicked(self) -> None: datasets_to_download = [] for name, item in self.get_items().items(): if item.checkState(0) == Qt.Checked: datasets_to_download.append(name) # TODO report progress thread = TaskThread( lambda: self.download_datasets(datasets_to_download)) thread.started.connect(self.on_started_download) thread.finished.connect(self.on_finished_download) thread.succeeded.connect(self.on_successful_download) thread.failed.connect(reraise) thread.start() def on_started_download(self): self.download_button.hide() self.progress_bar.show() self.tree_widget.setEnabled(False) def on_finished_download(self) -> None: self.download_button.show() self.progress_bar.hide() self.tree_widget.setEnabled(True) self.tree_widget.clear() self.populate_tree() Broadcast.geo_datasets_updated.emit() def on_successful_download(self) -> None: self.msg_bar.success('Geographical datasets downloaded successfully.') def download_datasets(self, dataset_names: List[str]) -> None: for name in dataset_names: download_and_extract_geo_dataset(name, self.options.geog_dir)