class SaveQueryDialog(QDialog, Ui_ui_save_query): # Signal new query signal_new_query_successful = pyqtSignal( name='signal_new_query_successful') def __init__( self, parent=None, query=None, white_list_values=None, output_geometry_types=None): """ SaveQueryDialog constructor @param query:query to save @type query: str @param white_list_values: doc of layers with columns @type white_list_values: dic @param output_geometry_types: list of layers @type output_geometry_types: list """ super(SaveQueryDialog, self).__init__(parent) QDialog.__init__(self) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar) self.whiteListValues = white_list_values self.outputGeomTypes = output_geometry_types self.query = query def accept(self): """ On accept, we call the FileQueryWriter """ category = self.lineEdit_category.text() name = self.lineEdit_name.text() # Get folder .qgis2/QuickOSM/queries on linux for instance folder = get_user_query_folder() ini_file = FileQueryWriter( path=folder, name=name, category=category, query=self.query, whiteListValues=self.whiteListValues, outputGeomTypes=self.outputGeomTypes) try: ini_file.save() self.signal_new_query_successful.emit() self.hide() except QuickOsmException, e: self.bar.pushMessage(e.msg, level=e.level, duration=e.duration) except Exception, e: self.display_exception(e)
class QgisServerCheckerDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(QgisServerCheckerDialog, 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.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed ) self.layout().insertWidget(0, self.bar)
class EditorTab(QWidget): def __init__(self, parent, parentConsole, filename, *args): QWidget.__init__(self, parent=None, *args) self.tw = parent self.pc = parentConsole self.path = None self.fileExcuteList = {} self.fileExcuteList = dict() self.newEditor = Editor(self) if filename: self.path = filename if os.path.exists(filename): self.loadFile(filename, False) # Creates layout for message bar self.layout = QGridLayout(self.newEditor) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) self.tabLayout = QGridLayout(self) self.tabLayout.setContentsMargins(0, 0, 0, 0) self.tabLayout.addWidget(self.newEditor) self.keyFilter = KeyFilter(parent, self) self.setEventFilter(self.keyFilter) def loadFile(self, filename, modified): try: fn = open(unicode(filename), "rb") except IOError, error: IOErrorTr = QCoreApplication.translate('PythonConsole', 'The file <b>%1</b> could not be opened. Error: %2') \ .arg(filename).arg(error.strerror) print IOErrorTr QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) txt = fn.read() fn.close() self.newEditor.setText(txt) QApplication.restoreOverrideCursor() self.newEditor.setModified(modified) self.newEditor.mtime = os.stat(filename).st_mtime self.newEditor.recolor()
class MyDialog(QDialog): def __init__(self): QDialog.__init__(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) self.buttonbox.accepted.connect(self.run) self.layout().addWidget(self.buttonbox, 0, 0, 2, 1) self.layout().addWidget(self.bar, 0, 0, 1, 1) def run(self): self.bar.pushMessage("Hello", "World", level=QgsMessageBar.INFO)
class GdalAlgorithmDialog(AlgorithmDialog): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.setMainWidget(GdalParametersPanel(self, alg)) self.runAsBatchButton = QPushButton(QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…")) self.runAsBatchButton.clicked.connect(self.runAsBatch) self.buttonBox.addButton(self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment self.mainWidget.parametersHaveChanged()
class GdalAlgorithmDialog(AlgorithmDialog): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.setMainWidget(GdalParametersPanel(self, alg)) cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") runAsBatchButton = QPushButton(self.tr("Run as batch process...")) runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(runAsBatchButton) cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(cornerWidget) self.mainWidget.parametersHaveChanged()
class ResourceSharingDialog(QDialog, FORM_CLASS): TAB_ALL = 0 TAB_INSTALLED = 1 TAB_SETTINGS = 2 def __init__(self, parent=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(ResourceSharingDialog, self).__init__(parent) self.setupUi(self) # Reconfigure UI self.setWindowTitle(f"{__title__} - {__version__}") self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) self.button_install.setEnabled(False) self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Set up the "main menu" - QListWidgetItem # All collections icon_all = QIcon() icon_all.addFile(str(resources_path("img", "plugin.svg")), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr("All collections")) # Installed collections icon_installed = QIcon() icon_installed.addFile( str(resources_path("img", "plugin-installed.svg")), QSize(), QIcon.Normal, QIcon.Off, ) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr("Installed collections")) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings / repositories icon_settings = QIcon() icon_settings.addFile(str(resources_path("img", "settings.svg")), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr("Settings")) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the items to the list widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Progress dialog for long running processes self.progress_dialog = None # Init the repository manager dialog self.repository_manager = RepositoryManager() self.collection_manager = CollectionManager() # Collections list view self.collections_model = QStandardItemModel(0, 1) self.collections_model.sort(0, Qt.AscendingOrder) self.collection_proxy = CustomSortFilterProxyModel(self) self.collection_proxy.setSourceModel(self.collections_model) self.list_view_collections.setModel(self.collection_proxy) # Active selected collection self._sel_coll_id = None # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.button_reload.clicked.connect(self.reload_repositories) self.button_reload_dir.clicked.connect(self.reload_off_res_directory) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) self.list_view_collections.selectionModel().currentChanged.connect( self.on_list_view_collections_clicked) self.line_edit_filter.textChanged.connect(self.filter_collections) self.button_install.clicked.connect(self.install_collection) self.button_open.clicked.connect(self.open_collection) self.button_uninstall.clicked.connect(self.uninstall_collection) self.button_box.button(QDialogButtonBox.Help).clicked.connect( self.open_help) # Populate the repositories widget and collections list view self.populate_repositories_widget() self.reload_collections_model() def set_current_tab(self, index): """Set stacked widget based on the active tab. :param index: The index of the active widget (in the list widget). :type index: int """ # Clear message bar self.message_bar.clearWidgets() if index == (self.menu_list_widget.count() - 1): # Last menu entry - Settings self.stacked_menu_widget.setCurrentIndex(1) else: # Not settings, must be Collections (all or installed) if index == 1: # Installed collections self.collection_proxy.accepted_status = COLLECTION_INSTALLED_STATUS # Set the web view title = self.tr("Installed Collections") description = self.tr( "On the left you see the list of all the " "installed collections.") else: # All collections (0) self.collection_proxy.accepted_status = COLLECTION_ALL_STATUS # Set the web view title = self.tr("All Collections") description = self.tr( "On the left you see a list of all the collections " "that are available from the registered repositories.<br> " "Installed collections are emphasized (in <b>bold</b>).") context = { "resources_path": str(resources_path()), "title": title, "description": description, } self.web_view_details.setHtml( render_template("tab_description.html", context)) self.stacked_menu_widget.setCurrentIndex(0) def add_repository(self): """Open add repository dialog.""" dlg = ManageRepositoryDialog(self) if not dlg.exec_(): return for repoName, repo in self.repository_manager.directories.items(): if dlg.line_edit_url.text().strip() == repo["url"]: self.message_bar.pushMessage( self.tr( "Unable to add another repository with the same URL!"), Qgis.Warning, 5, ) return if dlg.line_edit_name.text().strip() == repoName: self.message_bar.pushMessage( self.tr("Repositories must have unique names!"), Qgis.Warning, 5) return repo_name = dlg.line_edit_name.text() repo_url = dlg.line_edit_url.text().strip() repo_auth_cfg = dlg.line_edit_auth_id.text().strip() if repo_name in self.repository_manager.directories: repo_name += "(2)" # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Add repository try: status, adderror = self.repository_manager.add_directory( repo_name, repo_url, repo_auth_cfg) if status: self.message_bar.pushMessage( self.tr("Repository was successfully added"), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr("Unable to add repository: %s") % adderror, Qgis.Warning, 5) except Exception as e: self.message_bar.pushMessage(self.tr("%s") % e, Qgis.Warning, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate edit and delete button self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def edit_repository(self): """Open edit repository dialog.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it is among the officially approved QGIS repositories settings = QgsSettings() settings.beginGroup(repo_settings_group()) if (settings.value(repo_name + "/url") in self.repository_manager._online_directories.values()): self.message_bar.pushMessage( self.tr("You can not edit the official repositories!"), Qgis.Warning, 5) return dlg = ManageRepositoryDialog(self) dlg.line_edit_name.setText(repo_name) dlg.line_edit_url.setText( self.repository_manager.directories[repo_name]["url"]) dlg.line_edit_auth_id.setText( self.repository_manager.directories[repo_name]["auth_cfg"]) if not dlg.exec_(): return # Check if the changed URL is already present and that # the new repository name is unique new_url = dlg.line_edit_url.text().strip() old_url = self.repository_manager.directories[repo_name]["url"] new_name = dlg.line_edit_name.text().strip() for repoName, repo in self.repository_manager.directories.items(): if new_url == repo["url"] and (old_url != new_url): self.message_bar.pushMessage( self.tr("Unable to add another repository with the same " "URL!"), Qgis.Warning, 5, ) return if new_name == repoName and (repo_name != new_name): self.message_bar.pushMessage( self.tr("Repositories must have unique names!"), Qgis.Warning, 5) return # Redundant if (new_name in self.repository_manager.directories) and (new_name != repo_name): new_name += "(2)" new_auth_cfg = dlg.line_edit_auth_id.text() # Show progress dialog self.show_progress_dialog("Fetching repository's metadata") # Edit repository try: status, editerror = self.repository_manager.edit_directory( repo_name, new_name, old_url, new_url, new_auth_cfg) if status: self.message_bar.pushMessage( self.tr("Repository is successfully updated"), Qgis.Success, 5) else: self.message_bar.pushMessage( self.tr("Unable to edit repository: %s") % editerror, Qgis.Warning, 5, ) except Exception as e: self.message_bar.pushMessage(self.tr("%s") % e, Qgis.Warning, 5) finally: self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() # Deactivate the edit and delete buttons self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def delete_repository(self): """Delete a repository in the tree widget.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it is among the offical repositories repo_url = self.repository_manager.directories[repo_name]["url"] if repo_url in self.repository_manager._online_directories.values(): self.message_bar.pushMessage( self.tr("You can not remove official repositories!"), Qgis.Warning, 5) return warning = (self.tr("Are you sure you want to remove the following " "repository?") + "\n" + repo_name) if (QMessageBox.warning( self, self.tr("QGIS Resource Sharing"), warning, QMessageBox.Yes, QMessageBox.No, ) == QMessageBox.No): return # Remove repository installed_collections = self.collection_manager.get_installed_collections( repo_url) if installed_collections: message = ("You have installed collections from this " "repository. Please uninstall them first!") self.message_bar.pushMessage(message, Qgis.Warning, 5) else: self.repository_manager.remove_directory(repo_name) # Reload data and widget self.reload_data_and_widget() # Deactivate the edit and delete buttons self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def reload_off_res_directory(self): """Slot called when the user clicks the 'Reload directory' button.""" # Show progress dialog self.show_progress_dialog("Reloading the official QGIS resource" " directory") self.repository_manager._online_directories = {} # Registered directories self.repository_manager._directories = {} self.repository_manager.fetch_online_directories() # Load directory of repositories from settings self.repository_manager.load_directories() self.message_bar.pushMessage("On-line directory reloaded", Qgis.Info, 5) self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() def reload_repositories(self): """Slot called when the user clicks the 'Reload repositories' button.""" # Show progress dialog self.show_progress_dialog("Reloading all repositories") for repo_name in self.repository_manager.directories: directory = self.repository_manager.directories[repo_name] url = directory["url"] auth_cfg = directory["auth_cfg"] try: status, reloaderror = self.repository_manager.reload_directory( repo_name, url, auth_cfg) if status: self.message_bar.pushMessage( self.tr("Repository %s is successfully reloaded") % repo_name, Qgis.Info, 5, ) else: self.message_bar.pushMessage( self.tr("Unable to reload %s: %s") % (repo_name, reloaderror), Qgis.Warning, 5, ) except Exception as e: self.message_bar.pushMessage( self.tr("%s") % e, Qgis.Warning, 5) self.progress_dialog.hide() # Reload data and widget self.reload_data_and_widget() def install_collection(self): """Slot called when the user clicks the Install/Reinstall button.""" # Save the current index to enable selection after installation self.current_index = self.list_view_collections.currentIndex() self.show_progress_dialog("Starting installation...") self.progress_dialog.canceled.connect(self.install_canceled) self.installer_thread = QThread() self.installer_worker = CollectionInstaller(self.collection_manager, self._sel_coll_id) self.installer_worker.moveToThread(self.installer_thread) self.installer_worker.finished.connect(self.install_finished) self.installer_worker.aborted.connect(self.install_aborted) self.installer_worker.progress.connect(self.install_progress) self.installer_thread.started.connect(self.installer_worker.run) self.installer_thread.start() def install_finished(self): # Process the result self.progress_dialog.hide() installStatus = self.installer_worker.install_status if not installStatus: message = self.installer_worker.error_message # Clean up the worker and thread self.installer_worker.deleteLater() self.installer_thread.quit() self.installer_thread.wait() self.installer_thread.deleteLater() if installStatus: self.reload_collections_model() # Report what has been installed message = "<b>%s</b> was successfully installed, " "containing:\n<ul>" % ( config.COLLECTIONS[self._sel_coll_id]["name"]) number = 0 for type_, description in SUPPORTED_RESOURCES_MAP.items(): if type_ in config.COLLECTIONS[self._sel_coll_id].keys(): number = config.COLLECTIONS[self._sel_coll_id][type_] message += (f"\n<li>{number} {description}" f'{"s" if number > 1 else ""}' f"</li>") message += "\n</ul>" QMessageBox.information(self, "Resource Sharing", message) self.populate_repositories_widget() # Set the selection oldRow = self.current_index.row() newIndex = self.collections_model.createIndex(oldRow, 0) selection_model = self.list_view_collections.selectionModel() selection_model.setCurrentIndex(newIndex, selection_model.ClearAndSelect) selection_model.select(newIndex, selection_model.ClearAndSelect) # Update the buttons self.button_install.setEnabled(True) self.button_install.setText("Reinstall") self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) self.show_collection_metadata(self._sel_coll_id) def install_canceled(self): self.progress_dialog.hide() self.show_progress_dialog("Cancelling installation...") self.installer_worker.abort() def install_aborted(self): if self.installer_thread.isRunning(): self.installer_thread.quit() self.installer_thread.finished.connect(self.progress_dialog.hide) def install_progress(self, text): self.progress_dialog.setLabelText(text) def uninstall_collection(self): """Slot called when the user clicks the 'Uninstall' button.""" # get the QModelIndex for the item to be uninstalled uninstall_index = self.list_view_collections.currentIndex() coll_id = self._sel_coll_id try: self.collection_manager.uninstall(coll_id) except Exception as e: LOGGER.error("Could not uninstall collection " + config.COLLECTIONS[coll_id]["name"] + ":\n" + str(e)) else: QMessageBox.information( self, "Resource Sharing", "The collection was successfully uninstalled!") self.reload_collections_model() # Fix the GUI currentMenuRow = self.menu_list_widget.currentRow() self.set_current_tab(currentMenuRow) self.populate_repositories_widget() rowCount = self.collection_proxy.rowCount() if rowCount > 0: # Set the current (and selected) row in the listview newRow = uninstall_index.row() # Check if this was the last element rowCount = self.collection_proxy.rowCount() if newRow == rowCount: newRow = newRow - 1 # Select the new current element newIndex = self.collections_model.createIndex(newRow, 0) selection_model = self.list_view_collections.selectionModel() selection_model.setCurrentIndex(newIndex, selection_model.ClearAndSelect) # Get the id of the current collection proxyModel = self.list_view_collections.model() proxyIndex = proxyModel.index(newRow, 0) current_coll_id = proxyIndex.data(COLLECTION_ID_ROLE) self._sel_coll_id = current_coll_id # Update buttons status = config.COLLECTIONS[current_coll_id]["status"] if status == COLLECTION_INSTALLED_STATUS: self.button_install.setEnabled(True) self.button_install.setText("Reinstall") self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) else: self.button_install.setEnabled(True) self.button_install.setText("Install") self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Update the web_view_details frame self.show_collection_metadata(current_coll_id) else: self.button_install.setEnabled(False) self.button_install.setText("Install") self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) def open_collection(self): """Slot called when the user clicks the 'Open' button.""" collection_path = local_collection_path(self._sel_coll_id) directory_url = QUrl.fromLocalFile(str(collection_path)) QDesktopServices.openUrl(directory_url) def reload_data_and_widget(self): """Reload repositories and collections and update widgets related.""" self.reload_repositories_widget() self.reload_collections_model() def reload_repositories_widget(self): """Refresh tree repositories using new repositories data.""" self.repository_manager.load_directories() self.populate_repositories_widget() def populate_repositories_widget(self): """Populate the current dictionary repositories to the tree widget.""" # Clear the current tree widget self.tree_repositories.clear() installed_collections = self.collection_manager.get_installed_collections( ) # Export the updated ones from the repository manager repo_Font = QFont() repo_with_installed_Font = QFont() repo_with_installed_Font.setWeight(60) collection_brush = QBrush(Qt.darkGray) installed_collection_brush = QBrush(QColor(60, 25, 10)) for repo_name in self.repository_manager.directories: url = self.repository_manager.directories[repo_name]["url"] item = QTreeWidgetItem(self.tree_repositories, REPOSITORY_ITEM) # Is the repository in the QGIS resource directory? if url in self.repository_manager._online_directories.values(): repo_with_installed_Font.setUnderline(True) repo_Font.setUnderline(True) else: repo_with_installed_Font.setUnderline(False) repo_Font.setUnderline(False) item.setText(0, repo_name) item.setText(1, url) item.setFont(0, repo_Font) for coll_id in config.COLLECTIONS: if ("repository_name" in config.COLLECTIONS[coll_id].keys() and config.COLLECTIONS[coll_id]["repository_name"] == repo_name): coll_name = config.COLLECTIONS[coll_id]["name"] coll_tags = config.COLLECTIONS[coll_id]["tags"] collectionItem = QTreeWidgetItem(item, COLLECTION_ITEM) brush = collection_brush collectionFont = QFont() collectionFont.setStyle(QFont.StyleItalic) collitemtext = coll_name if (installed_collections and coll_id in installed_collections.keys()): collitemtext = coll_name + " (installed)" brush = installed_collection_brush item.setFont(0, repo_with_installed_Font) item.setForeground(0, brush) item.setForeground(1, brush) collectionItem.setFont(0, collectionFont) collectionItem.setForeground(0, brush) collectionItem.setText(0, collitemtext) collectionItem.setFont(1, collectionFont) collectionItem.setForeground(1, brush) collectionItem.setText(1, coll_tags) self.tree_repositories.resizeColumnToContents(0) self.tree_repositories.resizeColumnToContents(1) self.tree_repositories.sortItems(1, Qt.AscendingOrder) def reload_collections_model(self): """Reload the collections model with the current collections.""" self.collections_model.clear() installed_collections = self.collection_manager.get_installed_collections( ) for id in config.COLLECTIONS: collection_name = config.COLLECTIONS[id]["name"] collection_author = config.COLLECTIONS[id]["author"] collection_tags = config.COLLECTIONS[id]["tags"] collection_description = config.COLLECTIONS[id]["description"] collection_status = config.COLLECTIONS[id]["status"] repository_name = "" if "repository_name" in config.COLLECTIONS[id].keys(): repository_name = config.COLLECTIONS[id]["repository_name"] item = QStandardItem(collection_name + " (" + repository_name + ")") item.setEditable(False) item.setData(id, COLLECTION_ID_ROLE) item.setData(collection_name, COLLECTION_NAME_ROLE) item.setData(collection_description, COLLECTION_DESCRIPTION_ROLE) item.setData(collection_author, COLLECTION_AUTHOR_ROLE) item.setData(collection_tags, COLLECTION_TAGS_ROLE) item.setData(collection_status, COLLECTION_STATUS_ROLE) # Make installed collections stand out if installed_collections and id in installed_collections.keys(): collectionFont = QFont() collectionFont.setWeight(60) item.setFont(collectionFont) self.collections_model.appendRow(item) self.collections_model.sort(0, Qt.AscendingOrder) def on_tree_repositories_itemSelectionChanged(self): """Slot for the itemSelectionChanged signal of tree_repositories.""" selected_item = self.tree_repositories.currentItem() if selected_item and selected_item.type() == REPOSITORY_ITEM: if selected_item: repo_name = selected_item.text(0) if not repo_name: return if repo_name not in self.repository_manager.directories.keys(): return repo_url = self.repository_manager.directories[repo_name]["url"] # Disable the edit and delete buttons for "official" repositories if repo_url in self.repository_manager._online_directories.values( ): self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) else: # Activate the edit and delete buttons self.button_edit.setEnabled(True) self.button_delete.setEnabled(True) elif selected_item and selected_item.type() == COLLECTION_ITEM: self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) else: self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) def on_list_view_collections_clicked(self, index): """Slot called when the user clicks an item in list_view_collections.""" real_index = self.collection_proxy.mapToSource(index) if real_index.row() != -1: collection_item = self.collections_model.itemFromIndex(real_index) collection_id = collection_item.data(COLLECTION_ID_ROLE) self._sel_coll_id = collection_id # Enable / disable buttons status = config.COLLECTIONS[self._sel_coll_id]["status"] is_installed = status == COLLECTION_INSTALLED_STATUS if is_installed: self.button_install.setEnabled(True) self.button_install.setText("Reinstall") self.button_open.setEnabled(True) self.button_uninstall.setEnabled(True) else: self.button_install.setEnabled(True) self.button_install.setText("Install") self.button_open.setEnabled(False) self.button_uninstall.setEnabled(False) # Show metadata self.show_collection_metadata(collection_id) @pyqtSlot(str) def filter_collections(self, text): search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.collection_proxy.setFilterRegExp(search) def show_collection_metadata(self, id): """Show the collection metadata given the ID.""" html = self.collection_manager.get_html(id) self.web_view_details.setHtml(html) def reject(self): """Slot called when the dialog is closed.""" # Serialize collections to settings self.repository_manager.serialize_repositories() self.done(0) def open_help(self): """Open help.""" doc_url = QUrl("http://qgis-contribution.github.io/" + "QGIS-ResourceSharing/") QDesktopServices.openUrl(doc_url) def show_progress_dialog(self, text): """Show infinite progress dialog with given text. :param text: Text as the label of the progress dialog :type text: str """ if self.progress_dialog is None: self.progress_dialog = QProgressDialog(self) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.setAutoClose(False) title = self.tr("Resource Sharing") self.progress_dialog.setWindowTitle(title) # Just use an infinite progress bar here self.progress_dialog.setMaximum(0) self.progress_dialog.setMinimum(0) self.progress_dialog.setValue(0) self.progress_dialog.setLabelText(text) self.progress_dialog.show()
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla,self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.setLexers() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretLineBackgroundColor(QColor("#fcf3ed")) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python %1 on %2\n" "## Type help(iface) for more info and list of methods.\n").arg(sys.version, socket.gethostname()) initText = self.setText(txtInit) def refreshLexerProperties(self): self.setLexers() def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace").toString() fontSize = self.settings.value("pythonConsole/fontsize", 10).toInt()[0] font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setColor(Qt.red, 1) self.lexer.setColor(Qt.darkGreen, 5) self.lexer.setColor(Qt.darkBlue, 15) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") hideToolBar = menu.addAction(iconHideTool, "Hide/Show Toolbar", self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction("Show Editor", self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, "Enter Selected", self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, "Clear console", self.clearConsole) menu.addSeparator() copyAction = menu.addAction("Copy", self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction("Select All", self.selectAll, QKeySequence.SelectAll) menu.addSeparator() settingsDialog = menu.addAction(iconSettings, "Settings", self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) action = menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if txt.length() and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle(self.tr('Batch Processing - %s') % self.alg.name) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): self.algs = [] self.load = [] self.canceled = False for row in range(self.mainWidget.tblParameters.rowCount()): alg = self.alg.getCopy() col = 0 for param in alg.parameters: if param.hidden: continue wrapper = self.mainWidget.wrappers[row][col] if not self.mainWidget.setParamValue(param, wrapper, alg): self.bar.pushMessage("", self.tr('Wrong or missing parameter value: %s (row %d)') % (param.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return col += 1 for out in alg.outputs: if out.hidden: continue widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if text.strip() != '': out.value = text col += 1 else: self.bar.pushMessage("", self.tr('Wrong or missing output value: %s (row %d)') % (out.description, row + 1), level=QgsMessageBar.WARNING, duration=5) self.algs = None return self.algs.append(alg) if self.alg.getVisibleOutputsCount(): widget = self.mainWidget.tblParameters.cellWidget(row, col) self.load.append(widget.currentIndex() == 0) else: self.load.append(False) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.progressBar.setMaximum(len(self.algs)) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass for count, alg in enumerate(self.algs): self.setText(self.tr('\nProcessing algorithm %d/%d...') % (count + 1, len(self.algs))) self.setInfo(self.tr('<b>Algorithm %s starting...</b>' % alg.name)) if runalg(alg, self) and not self.canceled: if self.load[count]: handleAlgorithmResults(alg, self, False) self.setInfo(self.tr('Algorithm %s correctly executed...') % alg.name) else: QApplication.restoreOverrideCursor() return self.finish() def finish(self): for count, alg in enumerate(self.algs): self.loadHTMLResults(alg, count) self.createSummaryTable() QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, alg, num): for out in alg.outputs: if out.hidden or not out.open: continue if isinstance(out, OutputHTML): ProcessingResults.addResult( '{} [{}]'.format(out.description, num), out.value) def createSummaryTable(self): createTable = False for out in self.algs[0].outputs: if isinstance(out, (OutputNumber, OutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for alg in self.algs: f.write('<hr>\n') for out in alg.outputs: if isinstance(out, (OutputNumber, OutputString)): f.write('<p>{}: {}</p>\n'.format(out.description, out.value)) f.write('<hr>\n') ProcessingResults.addResult( '{} [summary]'.format(self.algs[0].name), outputFile)
class Qgis2threejsDialog(QDialog): STYLE_MAX_COUNT = 3 def __init__(self, iface): QDialog.__init__(self, iface.mainWindow()) self.iface = iface self.apiChanged22 = False # not QgsApplication.prefixPath().startswith("C:/OSGeo4W") # QGis.QGIS_VERSION_INT >= 20200 # Set up the user interface from Designer. self.ui = ui = Ui_Qgis2threejsDialog() ui.setupUi(self) self.setWindowFlags(self.windowFlags() | Qt.WindowMinimizeButtonHint) ui.lineEdit_OutputFilename.setPlaceholderText("[Temporary file]") ui.pushButton_Run.clicked.connect(self.run) ui.pushButton_Close.clicked.connect(self.reject) # DEM tab ui.toolButton_switchFocusMode.setVisible(False) ui.toolButton_PointTool.setVisible(False) ui.progressBar.setVisible(False) self.switchFocusMode(True) ui.toolButton_Browse.clicked.connect(self.browseClicked) ui.radioButton_Simple.toggled.connect(self.samplingModeChanged) ui.horizontalSlider_Resolution.valueChanged.connect(self.calculateResolution) ui.spinBox_Height.valueChanged.connect(self.updateQuads) ui.toolButton_switchFocusMode.clicked.connect(self.switchFocusModeClicked) ui.toolButton_PointTool.clicked.connect(self.startPointSelection) # Vector tab ui.treeWidget_VectorLayers.setHeaderLabel("Vector layers") self.initVectorStyleWidgets() ui.treeWidget_VectorLayers.currentItemChanged.connect(self.currentVectorLayerChanged) ui.treeWidget_VectorLayers.itemChanged.connect(self.vectorLayerItemChanged) ui.comboBox_ObjectType.currentIndexChanged.connect(self.objectTypeSelectionChanged) self.bar = None self.localBrowsingMode = True self.rb_quads = self.rb_point = None self.currentVectorLayer = None self.vectorPropertiesDict = {} self.objectTypeManager = ObjectTypeManager() # set map tool self.previousMapTool = None self.mapTool = RectangleMapTool(iface.mapCanvas()) self.connect(self.mapTool, SIGNAL("rectangleCreated()"), self.rectangleSelected) # self.mapTool = PointMapTool(iface.mapCanvas()) # QObject.connect(self.mapTool, SIGNAL("pointSelected()"), self.pointSelected) iface.mapCanvas().mapToolSet.connect(self.mapToolSet) self.startPointSelection() def exec_(self): ui = self.ui messages = [] # show message if crs unit is degrees mapSettings = self.iface.mapCanvas().mapSettings() if self.apiChanged22 else self.iface.mapCanvas().mapRenderer() if mapSettings.destinationCrs().mapUnits() in [QGis.Degrees]: self.showMessageBar("The unit of current CRS is degrees", "Terrain may not appear well.") # show message if there are no dem layer no_demlayer = ui.comboBox_DEMLayer.count() == 0 ui.pushButton_Run.setEnabled(not no_demlayer) if no_demlayer: self.showMessageBar("No DEM layer", "Load 1-band raster layer with GDAL provider.", QgsMessageBar.WARNING) return QDialog.exec_(self) def showMessageBar(self, title, text, level=QgsMessageBar.INFO): if self.bar is None: self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) ui = self.ui margins = ui.gridLayout.getContentsMargins() vl = ui.gridLayout.takeAt(0) ui.gridLayout.setContentsMargins(0,0,0,0) ui.gridLayout.addWidget(self.bar, 0, 0) ui.gridLayout.addItem(vl, 1, 0) ui.verticalLayout.setContentsMargins(margins[0], margins[1] / 2, margins[2], margins[3]) self.bar.pushMessage(title, text, level=level) def initDEMLayerList(self, layerId=None): # list 1 band raster layers self.ui.comboBox_DEMLayer.clear() for id, layer in QgsMapLayerRegistry().instance().mapLayers().items(): if layer.type() == QgsMapLayer.RasterLayer and layer.providerType() == "gdal" and layer.bandCount() == 1: self.ui.comboBox_DEMLayer.addItem(layer.name(), id) # select the last selected layer if layerId is not None: index = self.ui.comboBox_DEMLayer.findData(layerId) if index != -1: self.ui.comboBox_DEMLayer.setCurrentIndex(index) return index return -1 def initVectorLayerTree(self, vectorPropertiesDict): self.vectorPropertiesDict = vectorPropertiesDict tree = self.ui.treeWidget_VectorLayers tree.clear() # add vector layers into tree widget self.treeTopItems = topItems = {QGis.Point:QTreeWidgetItem(tree, ["Point"]), QGis.Line:QTreeWidgetItem(tree, ["Line"]), QGis.Polygon:QTreeWidgetItem(tree, ["Polygon"])} self.vlItems = {} for layer in self.iface.legendInterface().layers(): if layer.type() != QgsMapLayer.VectorLayer: continue geometry_type = layer.geometryType() if geometry_type in [QGis.Point, QGis.Line, QGis.Polygon]: self.vlItems[layer.id()] = item = QTreeWidgetItem(topItems[geometry_type], [layer.name()]) if layer.id() in self.vectorPropertiesDict: isVisible = self.vectorPropertiesDict[layer.id()]["visible"] else: isVisible = False #self.iface.legendInterface().isLayerVisible(layer) check_state = Qt.Checked if isVisible else Qt.Unchecked item.setData(0, Qt.CheckStateRole, check_state) item.setData(0, Qt.UserRole, layer.id()) #item.setDisabled(True) #item.setData(0, Qt.CheckStateRole, Qt.Unchecked) for item in topItems.values(): tree.expandItem(item) self.setVectorStylesEnabled(False) def initVectorStyleWidgets(self): self.colorWidget = StyleWidget(StyleWidget.COLOR) self.ui.verticalLayout_Styles.addWidget(self.colorWidget) self.heightWidget = StyleWidget(StyleWidget.HEIGHT) self.ui.verticalLayout_zCoordinate.addWidget(self.heightWidget) self.styleWidgets = [] for i in range(self.STYLE_MAX_COUNT): widget = StyleWidget() widget.setVisible(False) self.ui.verticalLayout_Styles.addWidget(widget) self.styleWidgets.append(widget) def currentVectorLayerChanged(self, currentItem, previousItem): # save properties of previous item if previousItem is not None: layerid = previousItem.data(0, Qt.UserRole) if layerid is not None: self.saveVectorProperties(layerid) layerid = currentItem.data(0, Qt.UserRole) if layerid is None: self.currentVectorLayer = None return self.currentVectorLayer = layer = QgsMapLayerRegistry().instance().mapLayer(layerid) if layer is None: return for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].hide() obj_types = self.objectTypeManager.objectTypeNames(layer.geometryType()) ui = self.ui ui.comboBox_ObjectType.blockSignals(True) ui.comboBox_ObjectType.clear() ui.comboBox_ObjectType.addItems(obj_types) ui.comboBox_ObjectType.blockSignals(False) # set up property widgets self.objectTypeSelectionChanged() if layerid in self.vectorPropertiesDict: # restore properties self.restoreVectorProperties(layerid) self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def vectorLayerItemChanged(self, item, column): # update style form enablement currentItem = self.ui.treeWidget_VectorLayers.currentItem() if currentItem: self.setVectorStylesEnabled(currentItem.data(0, Qt.CheckStateRole) == Qt.Checked) def objectTypeSelectionChanged(self, idx=None): layer = self.currentVectorLayer try: ve = float(ui.lineEdit_zFactor.text()) except: ve = 1 mapTo3d = MapTo3D(self.iface.mapCanvas(), verticalExaggeration=ve) self.objectTypeManager.setupForm(self, mapTo3d, layer, layer.geometryType(), self.ui.comboBox_ObjectType.currentIndex()) def numericFields(self, layer): # get attributes of a sample feature and create numeric field name list numeric_fields = [] f = QgsFeature() layer.getFeatures().nextFeature(f) for field in f.fields(): isNumeric = False try: float(f.attribute(field.name())) isNumeric = True except: pass if isNumeric: numeric_fields.append(field.name()) return numeric_fields def setVectorStylesEnabled(self, enabled): self.ui.comboBox_ObjectType.setEnabled(enabled) self.ui.label_ObjectType.setEnabled(enabled) self.colorWidget.setEnabled(enabled) self.heightWidget.setEnabled(enabled) for i in range(self.STYLE_MAX_COUNT): self.styleWidgets[i].setEnabled(enabled) def saveVectorProperties(self, layerid): properties = {} layer = QgsMapLayerRegistry().instance().mapLayer(layerid) itemIndex = self.ui.comboBox_ObjectType.currentIndex() properties["itemindex"] = itemIndex properties["typeitem"] = self.objectTypeManager.objectTypeItem(layer.geometryType(), itemIndex) properties["visible"] = self.vlItems[self.currentVectorLayer.id()].data(0, Qt.CheckStateRole) == Qt.Checked properties["color"] = self.colorWidget.values() properties["height"] = self.heightWidget.values() for i in range(self.STYLE_MAX_COUNT): if self.styleWidgets[i].isVisible(): properties[i] = self.styleWidgets[i].values() self.vectorPropertiesDict[layerid] = properties def restoreVectorProperties(self, layerid): properties = self.vectorPropertiesDict[layerid] self.ui.comboBox_ObjectType.setCurrentIndex(properties["itemindex"]) self.colorWidget.setValues(properties["color"]) self.heightWidget.setValues(properties["height"]) for i in range(self.STYLE_MAX_COUNT): if i in properties: self.styleWidgets[i].setValues(properties[i]) def calculateResolution(self, v=None): extent = self.iface.mapCanvas().extent() renderer = self.iface.mapCanvas().mapRenderer() size = 100 * self.ui.horizontalSlider_Resolution.value() self.ui.label_Resolution.setText("about {0} x {0} px".format(size)) # calculate resolution and size width, height = renderer.width(), renderer.height() s = (size * size / float(width * height)) ** 0.5 if s < 1: width = int(width * s) height = int(height * s) xres = extent.width() / width yres = extent.height() / height self.ui.lineEdit_HRes.setText(str(xres)) self.ui.lineEdit_VRes.setText(str(yres)) self.ui.lineEdit_Width.setText(str(width + 1)) self.ui.lineEdit_Height.setText(str(height + 1)) def progress(self, percentage): self.ui.progressBar.setValue(percentage) self.ui.progressBar.setVisible(percentage != 100) def run(self): ui = self.ui filename = ui.lineEdit_OutputFilename.text() # ""=Temporary file if filename != "" and QFileInfo(filename).exists() and QMessageBox.question(None, "Qgis2threejs", "Output file already exists. Overwrite it?", QMessageBox.Ok | QMessageBox.Cancel) != QMessageBox.Ok: return self.endPointSelection() item = ui.treeWidget_VectorLayers.currentItem() if item: self.saveVectorProperties(item.data(0, Qt.UserRole)) ui.pushButton_Run.setEnabled(False) self.progress(0) canvas = self.iface.mapCanvas() htmlfilename = ui.lineEdit_OutputFilename.text() demlayerid = ui.comboBox_DEMLayer.itemData(ui.comboBox_DEMLayer.currentIndex()) mapTo3d = MapTo3D(canvas, verticalExaggeration=float(ui.lineEdit_zFactor.text())) if self.ui.radioButton_Simple.isChecked(): dem_width = int(ui.lineEdit_Width.text()) dem_height = int(ui.lineEdit_Height.text()) context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode, dem_width, dem_height) htmlfilename = runSimple(htmlfilename, context, self.progress) else: context = OutputContext(mapTo3d, canvas, demlayerid, self.vectorPropertiesDict, self.objectTypeManager, self.localBrowsingMode) htmlfilename = runAdvanced(htmlfilename, context, self, self.progress) self.progress(100) ui.pushButton_Run.setEnabled(True) if htmlfilename is None: return self.clearRubberBands() if not tools.openHTMLFile(htmlfilename): return QDialog.accept(self) def reject(self): self.endPointSelection() self.clearRubberBands() QDialog.reject(self) def startPointSelection(self): canvas = self.iface.mapCanvas() self.previousMapTool = canvas.mapTool() canvas.setMapTool(self.mapTool) self.ui.toolButton_PointTool.setVisible(False) def endPointSelection(self): self.mapTool.reset() self.iface.mapCanvas().setMapTool(self.previousMapTool) def rectangleSelected(self): ui = self.ui ui.radioButton_Advanced.setChecked(True) rect = self.mapTool.rectangle() toRect = rect.width() and rect.height() self.switchFocusMode(toRect) ui.lineEdit_xmin.setText(str(rect.xMinimum())) ui.lineEdit_ymin.setText(str(rect.yMinimum())) ui.lineEdit_xmax.setText(str(rect.xMaximum())) ui.lineEdit_ymax.setText(str(rect.yMaximum())) quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(rect, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), rect.center()) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def pointSelected(self): # set values of controls self.ui.lineEdit_CenterX.setText(str(self.mapTool.point.x())) self.ui.lineEdit_CenterY.setText(str(self.mapTool.point.y())) self.ui.radioButton_Advanced.setChecked(True) quadtree = QuadTree(self.iface.mapCanvas().extent(), self.mapTool.point, self.ui.spinBox_Height.value()) self.createRubberBands(quadtree.quads(), self.mapTool.point) self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive) def mapToolSet(self, mapTool): if mapTool != self.mapTool: self.ui.toolButton_PointTool.setVisible(True) def createQuadTree(self): ui = self.ui try: c = map(float, [ui.lineEdit_xmin.text(), ui.lineEdit_ymin.text(), ui.lineEdit_xmax.text(), ui.lineEdit_ymax.text()]) except: return None quadtree = QuadTree(self.iface.mapCanvas().extent()) quadtree.buildTreeByRect(QgsRectangle(c[0], c[1], c[2], c[3]), ui.spinBox_Height.value()) return quadtree def createRubberBands(self, quads, point=None): self.clearRubberBands() # create quads with rubber band self.rb_quads = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rb_quads.setColor(Qt.blue) self.rb_quads.setWidth(1) for quad in quads: points = [] extent = quad.extent points.append(QgsPoint(extent.xMinimum(), extent.yMinimum())) points.append(QgsPoint(extent.xMinimum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMaximum())) points.append(QgsPoint(extent.xMaximum(), extent.yMinimum())) self.rb_quads.addGeometry(QgsGeometry.fromPolygon([points]), None) self.log(extent.toString()) self.log("Quad count: %d" % len(quads)) # create a point with rubber band if point: self.rb_point = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb_point.setColor(Qt.red) self.rb_point.addPoint(point) def clearRubberBands(self): # clear quads and point if self.rb_quads: self.iface.mapCanvas().scene().removeItem(self.rb_quads) self.rb_quads = None if self.rb_point: self.iface.mapCanvas().scene().removeItem(self.rb_point) self.rb_point = None def browseClicked(self): directory = self.ui.lineEdit_OutputFilename.text() if directory == "": directory = QDir.homePath() filename = QFileDialog.getSaveFileName(self, self.tr("Output filename"), directory, "HTML file (*.html *.htm)", options=QFileDialog.DontConfirmOverwrite) if filename != "": self.ui.lineEdit_OutputFilename.setText(filename) def samplingModeChanged(self): ui = self.ui isSimpleMode = ui.radioButton_Simple.isChecked() simple_widgets = [ui.horizontalSlider_Resolution, ui.lineEdit_Width, ui.lineEdit_Height, ui.lineEdit_HRes, ui.lineEdit_VRes] for w in simple_widgets: w.setEnabled(isSimpleMode) isAdvancedMode = not isSimpleMode advanced_widgets = [ui.spinBox_Height, ui.lineEdit_xmin, ui.lineEdit_ymin, ui.lineEdit_xmax, ui.lineEdit_ymax, ui.toolButton_switchFocusMode] for w in advanced_widgets: w.setEnabled(isAdvancedMode) def updateQuads(self, v=None): quadtree = self.createQuadTree() if quadtree: self.createRubberBands(quadtree.quads(), quadtree.focusRect.center()) else: self.clearRubberBands() def switchFocusModeClicked(self): self.switchFocusMode(not self.ui.label_xmin.isVisible()) def switchFocusMode(self, toRect): ui = self.ui toPoint = not toRect ui.label_xmin.setVisible(toRect) ui.label_ymin.setVisible(toRect) ui.lineEdit_xmin.setVisible(toRect) ui.lineEdit_ymin.setVisible(toRect) suffix = "max" if toRect else "" ui.label_xmax.setText("x" + suffix) ui.label_ymax.setText("y" + suffix) mode = "point" if toRect else "rectangle" ui.toolButton_switchFocusMode.setText("To " + mode + " selection") selection = "area" if toRect else "point" action = "Stroke a rectangle" if toRect else "Click" ui.label_Focus.setText("Focus {0} ({1} on map canvas to set values)".format(selection, action)) def log(self, msg): if debug_mode: qDebug(msg)
class LoadByCategory(QtGui.QDialog, load_by_category_dialog.Ui_LoadByCategory): def __init__(self, parent=None): """Constructor.""" super(LoadByCategory, 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.filename = "" self.dbLoaded = False self.epsg = 0 self.crs = None self.categories = [] self.selectedClasses = [] self.point = [] self.line = [] self.polygon = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] #Sql factory generator self.isSpatialite = True self.setupUi(self) self.tabWidget.setCurrentIndex(0) self.factory = SqlGeneratorFactory() self.gen = self.factory.createSqlGenerator(self.isSpatialite) self.utils = Utils() self.parentTreeNode = None self.comboBoxPostgis.setCurrentIndex(0) self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) self.checkBoxAll.setCheckState(0) self.bar = QgsMessageBar() self.setLayout(QtGui.QGridLayout(self)) self.layout().setContentsMargins(0,0,0,0) self.layout().setAlignment(QtCore.Qt.AlignTop) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.bar.setSizePolicy(sizePolicy) self.layout().addWidget(self.bar, 0,0,1,1) #Objects Connections QtCore.QObject.connect(self.pushButtonOpenFile, QtCore.SIGNAL(("clicked()")), self.loadDatabase) QtCore.QObject.connect(self.pushButtonCancel, QtCore.SIGNAL(("clicked()")), self.cancel) QtCore.QObject.connect(self.pushButtonOk, QtCore.SIGNAL(("clicked()")), self.okSelected) QtCore.QObject.connect(self.tabWidget,QtCore.SIGNAL(("currentChanged(int)")), self.restoreInitialState) QtCore.QObject.connect(self.pushButtonSelectAll, QtCore.SIGNAL(("clicked()")), self.selectAll) QtCore.QObject.connect(self.pushButtonDeselectAll, QtCore.SIGNAL(("clicked()")), self.deselectAll) QtCore.QObject.connect(self.pushButtonSelectOne, QtCore.SIGNAL(("clicked()")), self.selectOne) QtCore.QObject.connect(self.pushButtonDeselectOne, QtCore.SIGNAL(("clicked()")), self.deselectOne) QtCore.QObject.connect(self.checkBoxAll, QtCore.SIGNAL(("stateChanged(int)")), self.setAllGroup) self.db = None #populating the postgis combobox self.populatePostGISConnectionsCombo() def __del__(self): self.closeDatabase() def closeDatabase(self): if self.db: self.db.close() self.db = None def restoreInitialState(self): self.filename = "" self.dbLoaded = False self.epsg = 0 self.crs = None self.categories = [] self.selectedClasses = [] self.spatialiteFileEdit.setText(self.filename) self.postGISCrsEdit.setText('') self.postGISCrsEdit.setReadOnly(True) self.spatialiteCrsEdit.setText('') self.spatialiteCrsEdit.setReadOnly(True) self.listWidgetCategoryFrom.clear() self.listWidgetCategoryTo.clear() self.point = [] self.line = [] self.polygon = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] self.parentTreeNode = None #Setting the database type if self.tabWidget.currentIndex() == 0: self.isSpatialite = True else: self.isSpatialite = False #getting the sql generator according to the database type self.gen = self.factory.createSqlGenerator(self.isSpatialite) self.comboBoxPostgis.setCurrentIndex(0) self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) self.checkBoxAll.setCheckState(0) def updateBDField(self): if self.dbLoaded == True: self.spatialiteFileEdit.setText(self.filename) else: self.filename = "" self.spatialiteFileEdit.setText(self.filename) def getDatabaseVersion(self): self.dbVersion = self.utils.getDatabaseVersion(self.db) self.qmlPath = self.utils.getQmlDir(self.db) def listCategoriesFromDatabase(self): self.listWidgetCategoryFrom.clear() self.listWidgetCategoryTo.clear() sql = self.gen.getTablesFromDatabase() query = QSqlQuery(sql, self.db) self.getDatabaseVersion() while query.next(): if self.isSpatialite: tableName = query.value(0) layerName = tableName split = tableName.split('_') if len(split) < 2: continue if self.dbVersion == '3.0' or self.dbVersion == '2.1.3': schema = split[0] category = split[1] categoryName = schema+'.'+category else: categoryName = split[0] #done this way to have back compatibility with spatialites already in production else: tableSchema = query.value(0) tableName = query.value(1) split = tableName.split('_') category = split[0] categoryName = tableSchema+'.'+category layerName = tableSchema+'.'+tableName if layerName.split("_")[-1] == "p": self.point.append(layerName) if layerName.split("_")[-1] == "l": self.line.append(layerName) if layerName.split("_")[-1] == "a": self.polygon.append(layerName) if tableName.split("_")[-1] == "p" or tableName.split("_")[-1] == "l" \ or tableName.split("_")[-1] == "a": self.insertIntoListView(categoryName) self.listWidgetCategoryFrom.sortItems() self.setCRS() def insertIntoListView(self, item_name): found = self.listWidgetCategoryFrom.findItems(item_name, Qt.MatchExactly) if len(found) == 0: item = QtGui.QListWidgetItem(item_name) self.listWidgetCategoryFrom.addItem(item) def selectAll(self): tam = self.listWidgetCategoryFrom.__len__() for i in range(tam+1,1,-1): item = self.listWidgetCategoryFrom.takeItem(i-2) self.listWidgetCategoryTo.addItem(item) self.listWidgetCategoryTo.sortItems() def deselectAll(self): tam = self.listWidgetCategoryTo.__len__() for i in range(tam+1,1,-1): item = self.listWidgetCategoryTo.takeItem(i-2) self.listWidgetCategoryFrom.addItem(item) self.listWidgetCategoryFrom.sortItems() def selectOne(self): listedItems = self.listWidgetCategoryFrom.selectedItems() for i in listedItems: item = self.listWidgetCategoryFrom.takeItem(self.listWidgetCategoryFrom.row(i)) self.listWidgetCategoryTo.addItem(item) self.listWidgetCategoryTo.sortItems() def deselectOne(self): listedItems = self.listWidgetCategoryTo.selectedItems() for i in listedItems: item = self.listWidgetCategoryTo.takeItem(self.listWidgetCategoryTo.row(i)) self.listWidgetCategoryFrom.addItem(item) self.listWidgetCategoryFrom.sortItems() def setAllGroup(self): if self.checkBoxAll.isChecked(): self.checkBoxPoint.setCheckState(2) self.checkBoxLine.setCheckState(2) self.checkBoxPolygon.setCheckState(2) else: self.checkBoxPoint.setCheckState(0) self.checkBoxLine.setCheckState(0) self.checkBoxPolygon.setCheckState(0) def setCRS(self): try: self.epsg = self.utils.findEPSG(self.db) if self.epsg == -1: self.bar.pushMessage("", self.tr("Coordinate Reference System not set or invalid!"), level=QgsMessageBar.WARNING) else: self.crs = QgsCoordinateReferenceSystem(self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId) if self.isSpatialite: self.spatialiteCrsEdit.setText(self.crs.description()) self.spatialiteCrsEdit.setReadOnly(True) else: self.postGISCrsEdit.setText(self.crs.description()) self.postGISCrsEdit.setReadOnly(True) except: pass @pyqtSlot(int) def on_comboBoxPostgis_currentIndexChanged(self): if self.comboBoxPostgis.currentIndex() > 0: self.loadDatabase() def loadDatabase(self): self.closeDatabase() if self.isSpatialite: (self.filename, self.db) = self.utils.getSpatialiteDatabase() if self.filename: self.spatialiteFileEdit.setText(self.filename) else: self.db = self.utils.getPostGISDatabase(self.comboBoxPostgis.currentText()) try: if not self.db.open(): QgsMessageLog.logMessage(self.db.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL) else: self.dbLoaded = True self.listCategoriesFromDatabase() except: pass def populatePostGISConnectionsCombo(self): self.comboBoxPostgis.clear() self.comboBoxPostgis.addItem("Select Database") self.comboBoxPostgis.addItems(self.utils.getPostGISConnections()) def cancel(self): self.restoreInitialState() self.close() def getSelectedItems(self): lista = self.classesListWidget.selectedItems() self.selectedClasses = [] tam = len(lista) for i in range(tam): self.selectedClasses.append(lista[i].text()) self.selectedClasses.sort() def okSelected(self): try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) if self.checkBoxOnlyWithElements.isChecked(): self.setLayersWithElements() ponto = self.pointWithElement linha = self.lineWithElement area = self.polygonWithElement else: ponto = self.point linha = self.line area = self.polygon if self.db and self.crs and len(self.listWidgetCategoryTo)>0: categoriasSelecionadas = [] for i in range(self.listWidgetCategoryTo.__len__()): categoriasSelecionadas.append(self.listWidgetCategoryTo.item(i).text()) try: if self.checkBoxPoint.isChecked(): self.loadLayers('p',categoriasSelecionadas,ponto) if self.checkBoxLine.isChecked(): self.loadLayers('l',categoriasSelecionadas,linha) if self.checkBoxPolygon.isChecked(): self.loadLayers('a',categoriasSelecionadas,area) if self.checkBoxPoint.isChecked()== False and self.checkBoxLine.isChecked() == False and self.checkBoxPolygon.isChecked() == False: self.bar.pushMessage(self.tr("WARNING!"), self.tr("Please, select at least one type of layer!"), level=QgsMessageBar.WARNING) else: self.restoreInitialState() self.close() except: qgis.utils.iface.messageBar().pushMessage(self.tr("CRITICAL!"), self.tr("Problem loading the categories!"), level=QgsMessageBar.CRITICAL) pass else: if self.db and not self.crs: self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Could not determine the coordinate reference system!"), level=QgsMessageBar.CRITICAL) if not self.db and not self.crs: self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Database not loaded properly!"), level=QgsMessageBar.CRITICAL) self.bar.pushMessage(self.tr("CRITICAL!"), self.tr("Could not determine the coordinate reference system!"), level=QgsMessageBar.CRITICAL) if len(self.listWidgetCategoryTo)==0: self.bar.pushMessage(self.tr("WARNING!"), self.tr("Please, select at least one category!"), level=QgsMessageBar.WARNING) categoriasSelecionadas = [] self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] QApplication.restoreOverrideCursor() except: QApplication.restoreOverrideCursor() def loadLayers(self, type, categories, layer_names): if self.isSpatialite: self.loadSpatialiteLayers(type, categories, layer_names) else: self.loadPostGISLayers(type, categories, layer_names) def setLayersWithElements(self): self.pointWithElement = [] self.lineWithElement = [] self.polygonWithElement = [] pontoAux = self.countElements(self.point) linhaAux = self.countElements(self.line) areaAux = self.countElements(self.polygon) for i in pontoAux: if i[1] > 0: self.pointWithElement.append(i[0]) for i in linhaAux: if i[1] > 0: self.lineWithElement.append(i[0]) for i in areaAux: if i[1] > 0: self.polygonWithElement.append(i[0]) def countElements(self, layers): listaQuantidades = [] for layer in layers: sql = self.gen.getElementCountFromLayer(layer) query = QSqlQuery(sql,self.db) query.next() number = query.value(0) if not query.exec_(sql): QgsMessageLog.logMessage(self.tr("Problem counting elements: ")+query.lastError().text(), "DSG Tools Plugin", QgsMessageLog.CRITICAL) listaQuantidades.append([layer, number]) return listaQuantidades def loadPostGISLayers(self, type, categories, layer_names): (database, host, port, user, password) = self.utils.getPostGISConnectionParameters(self.comboBoxPostgis.currentText()) uri = QgsDataSourceURI() uri.setConnection(str(host),str(port), str(database), str(user), str(password)) geom_column = 'geom' if self.parentTreeNode is None: self.parentTreeNode = qgis.utils.iface.legendInterface (). addGroup (database, -1) if type == 'p': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Ponto", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'l': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Linha", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'a': idGrupo = qgis.utils.iface.legendInterface (). addGroup ("Area", True,self.parentTreeNode) for categoria in categories: self.preparePostGISToLoad(uri, categoria, layer_names, idGrupo, geom_column) def preparePostGISToLoad(self, uri, categoria, layer_names, idGrupo, geom_column): idSubgrupo = qgis.utils.iface.legendInterface().addGroup(categoria, True, idGrupo) layer_names.sort(reverse=True) for layer_name in layer_names: split = layer_name.split('_') category = split[0] schema = category.split('.')[0] name = layer_name.replace(schema+'.', '') if category == categoria: sql = self.gen.loadLayerFromDatabase(layer_name) uri.setDataSource(schema, name, geom_column, sql, 'id') uri.disableSelectAtId(True) self.loadEDGVLayer(uri, name, 'postgres', idSubgrupo) def prepareSpatialiteToLoad(self, uri, categoria, layer_names, idGrupo, geom_column): idSubgrupo = qgis.utils.iface.legendInterface().addGroup(categoria, True, idGrupo) layer_names.sort(reverse=True) for layer_name in layer_names: split = layer_name.split('_') if self.dbVersion == '3.0' or self.dbVersion == '2.1.3': category = split[0]+'.'+split[1] else: category = split[0] if category == categoria: uri.setDataSource('', layer_name, geom_column) self.loadEDGVLayer(uri, layer_name, 'spatialite', idSubgrupo) def loadSpatialiteLayers(self, type, categories, layer_names): uri = QgsDataSourceURI() uri.setDatabase(self.filename) geom_column = 'GEOMETRY' if self.parentTreeNode is None: self.parentTreeNode = qgis.utils.iface.legendInterface(). addGroup(self.filename.split('.sqlite')[0].split('/')[-1], -1) if type == 'p': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Ponto", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'l': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Linha", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) if type == 'a': idGrupo = qgis.utils.iface.legendInterface(). addGroup("Area", True, self.parentTreeNode) for categoria in categories: self.prepareSpatialiteToLoad(uri, categoria, layer_names, idGrupo, geom_column) def loadEDGVLayer(self, uri, layer_name, provider, idSubgrupo): vlayer = QgsVectorLayer(uri.uri(), layer_name, provider) vlayer.setCrs(self.crs) QgsMapLayerRegistry.instance().addMapLayer(vlayer) #added due to api changes if self.isSpatialite and (self.dbVersion == '3.0' or self.dbVersion == '2.1.3'): lyr = '_'.join(layer_name.replace('\r', '').split('_')[1::]) else: lyr = layer_name.replace('\r','') vlayerQml = os.path.join(self.qmlPath, lyr+'.qml') vlayer.loadNamedStyle(vlayerQml, False) QgsMapLayerRegistry.instance().addMapLayer(vlayer) qgis.utils.iface.legendInterface().moveLayer(vlayer, idSubgrupo) if not vlayer.isValid(): QgsMessageLog.logMessage(vlayer.error().summary(), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
class ServerConnectionsWidget(BASE, WIDGET): def __init__(self): super(ServerConnectionsWidget, self).__init__() self.currentServer = None self.setupUi(self) self.addMenuToButtonNew() self.addAuthWidgets() self.buttonRemove.clicked.connect(self.buttonRemoveClicked) self.populateServers() self.listServers.currentItemChanged.connect(self.currentServerChanged) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.setCurrentServer(None) self.buttonSave.clicked.connect(self.saveButtonClicked) self.comboGeoserverDataStorage.currentIndexChanged.connect( self.geoserverDatastorageChanged) self.btnConnectGeoserver.clicked.connect(self.testConnectionGeoserver) self.btnConnectPostgis.clicked.connect(self.testConnectionPostgis) self.btnConnectGeocatLive.clicked.connect( self.testConnectionGeocatLive) self.btnConnectCsw.clicked.connect(self.testConnectionCsw) self.btnAddDatastore.clicked.connect(self.addPostgisDatastore) self.btnRefreshDatabases.clicked.connect( self.populatePostgisComboWithGeoserverPostgisServers) self.txtCswName.textChanged.connect(self._setCurrentServerHasChanges) self.txtCswNode.textChanged.connect(self._setCurrentServerHasChanges) self.txtGeoserverName.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisName.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeoserverUrl.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeocatLiveName.textChanged.connect( self._setCurrentServerHasChanges) self.txtCswUrl.textChanged.connect(self._setCurrentServerHasChanges) self.txtPostgisServerAddress.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisPort.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisSchema.textChanged.connect( self._setCurrentServerHasChanges) self.txtPostgisDatabase.textChanged.connect( self._setCurrentServerHasChanges) self.txtGeocatLiveIdentifier.textChanged.connect( self._setCurrentServerHasChanges) self.comboMetadataProfile.currentIndexChanged.connect( self._setCurrentServerHasChanges) self.comboGeoserverDatabase.currentIndexChanged.connect( self._setCurrentServerHasChanges) self.radioLocalPath.toggled.connect(self.mapserverStorageChanged) self.fileMapserver.setStorageMode(QgsFileWidget.GetDirectory) self.btnSaveServers.clicked.connect(self.saveServers) self.btnLoadServers.clicked.connect(self.loadServers) self.comboMetadataProfile.setVisible(False) self.labelMetadataProfile.setVisible(False) def checkServersHaveBeenDefined(self): if self.listServers.count(): self.txtNoServers.setVisible(False) self.listServers.setVisible(True) else: self.txtNoServers.setVisible(True) self.listServers.setVisible(False) def saveServers(self): filename = QFileDialog.getSaveFileName(self, self.tr("Save servers"), "", '*.json')[0] if filename: if not filename.endswith("json"): filename += ".json" with open(filename, "w") as f: f.write(serversAsJsonString()) def loadServers(self): filename = QFileDialog.getOpenFileName(self, self.tr("Load servers"), "", '*.json')[0] if filename: with open(filename) as f: servers = json.load(f) for server in servers: s = serverFromDefinition(server) if s.name not in allServers(): self.addServerItem(s) addServer(s) def geoserverDatastorageChanged(self): storage = self.comboGeoserverDataStorage.currentIndex() if storage == GeoserverServer.POSTGIS_MANAGED_BY_BRIDGE: self.populatePostgisComboWithPostgisServers() self.comboGeoserverDatabase.setVisible(True) self.btnAddDatastore.setVisible(False) self.labelGeoserverDatastore.setVisible(True) self.btnRefreshDatabases.setVisible(False) elif storage == GeoserverServer.POSTGIS_MANAGED_BY_GEOSERVER: self.comboGeoserverDatabase.setVisible(True) self.btnAddDatastore.setVisible(True) self.labelGeoserverDatastore.setVisible(True) self.btnRefreshDatabases.setVisible(True) self.populatePostgisComboWithGeoserverPostgisServers() else: self.comboGeoserverDatabase.setVisible(False) self.btnAddDatastore.setVisible(False) self.labelGeoserverDatastore.setVisible(False) self.btnRefreshDatabases.setVisible(False) self._setCurrentServerHasChanges() def addPostgisDatastore(self): url = self.txtGeoserverUrl.text().strip() server = self.createGeoserverServer() if server is None: self.bar.pushMessage(self.tr("Wrong values in server definition"), level=Qgis.Warning, duration=5) return dlg = NewDatasetDialog(self) dlg.exec_() name = dlg.name if name is None: return def _entry(k, v): return {"@key": k, "$": v} ds = { "dataStore": { "name": dlg.name, "type": "PostGIS", "enabled": True, "connectionParameters": { "entry": [ _entry("schema", dlg.schema), _entry("port", dlg.port), _entry("database", dlg.database), _entry("passwd", dlg.password), _entry("user", dlg.username), _entry("host", dlg.host), _entry("dbtype", "postgis") ] } } } try: datastores = execute(lambda: server.addPostgisDatastore(ds)) self.populatePostgisComboWithGeoserverPostgisServers() except: self.bar.pushMessage( self.tr("Could not create new PostGIS dataset"), level=Qgis.Warning, duration=5) def mapserverStorageChanged(self, checked): self.labelLocalFolder.setVisible(checked) self.labelRemoteFolder.setVisible(not checked) self.fileMapserver.setVisible(checked) self.txtRemoteFolder.setVisible(not checked) self.labelHost.setVisible(not checked) self.labelPort.setVisible(not checked) self.labelMapserverCredentials.setVisible(not checked) self.txtMapserverHost.setVisible(not checked) self.txtMapserverPort.setVisible(not checked) self.mapserverAuthWidget.setVisible(not checked) self._setCurrentServerHasChanges() def currentServerChanged(self, new, old): if new is None: self.setCurrentServer(new) return else: name = self.listServers.itemWidget(new).serverName() server = allServers()[name] if self.currentServer is not None and new is not None: if server.name == self.currentServer.name: return if self.currentServerHasChanges: res = QMessageBox.question( self, self.tr("Servers"), self.tr( "Do you want to save changes to the current server?"), QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) if res == QMessageBox.Yes: if not self.saveCurrentServer(): self.bar.pushMessage( self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) self.listServers.setCurrentItem(old) else: self.setCurrentServer(server) elif res == QMessageBox.Cancel: self.listServers.setCurrentItem(old) else: self.setCurrentServer(server) else: self.setCurrentServer(server) def _testConnection(self, server): if server is None: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) else: if execute(server.testConnection): self.bar.pushMessage( self.tr("Success"), self.tr("Connection succesfully established with server"), level=Qgis.Success, duration=5) else: self.bar.pushMessage(self.tr("Error"), self.tr("Could not connect with server"), level=Qgis.Warning, duration=5) def testConnectionPostgis(self): server = self.createPostgisServer() self._testConnection(server) def testConnectionGeoserver(self): server = self.createGeoserverServer() self._testConnection(server) def testConnectionGeocatLive(self): server = self.createGeocatLiveServer() self._testConnection(server) def testConnectionCsw(self): server = self.createGeonetworkServer() self._testConnection(server) def saveCurrentServer(self): w = self.stackedWidget.currentWidget() server = None if w == self.widgetEmpty: return True elif w == self.widgetGeoserver: server = self.createGeoserverServer() elif w == self.widgetMapserver: server = self.createMapserverServer() elif w == self.widgetPostgis: server = self.createPostgisServer() elif w == self.widgetMetadataCatalog: server = self.createGeonetworkServer() elif w == self.widgetGeocatLive: server = self.createGeocatLiveServer() if server is None: return False else: if self.currentServer is not None: removeServer(self.currentServer.name) item = self.itemFromServerName(self.currentServer.name) self.listServers.itemWidget(item).setServerName(server.name) addServer(server) self.currentServer = server return True def itemFromServerName(self, name): for i in range(self.listServers.count()): item = self.listServers.item(i) if name == self.listServers.itemWidget(item).serverName(): return item def createGeoserverServer(self): ##TODO check validity of name and values name = self.txtGeoserverName.text().strip() url = self.txtGeoserverUrl.text().strip() authid = self.geoserverAuth.configId() if not bool(authid): return None storage = self.comboGeoserverDataStorage.currentIndex() postgisdb = None if storage in [ GeoserverServer.POSTGIS_MANAGED_BY_BRIDGE, GeoserverServer.POSTGIS_MANAGED_BY_GEOSERVER ]: postgisdb = self.comboGeoserverDatabase.currentText() useOriginalDataSource = self.chkUseOriginalDataSource.isChecked() if "" in [name, url]: return None server = GeoserverServer(name, url, authid, storage, postgisdb, useOriginalDataSource) return server def createPostgisServer(self): ##TODO check validity of name and values name = self.txtPostgisName.text() host = self.txtPostgisServerAddress.text() port = self.txtPostgisPort.text() schema = self.txtPostgisSchema.text() database = self.txtPostgisDatabase.text() authid = self.postgisAuth.configId() server = PostgisServer(name, authid, host, port, schema, database) return server def createGeonetworkServer(self): ##TODO check validity of name and values name = self.txtCswName.text() node = self.txtCswNode.text() authid = self.cswAuth.configId() if bool(authid): url = self.txtCswUrl.text() profile = self.comboMetadataProfile.currentIndex() server = GeonetworkServer(name, url, authid, profile, node) return server def createMapserverServer(self): ##TODO check validity of name and values name = self.txtMapserverName.text() authid = self.mapserverAuth.configId() host = self.txtMapserverHost.text() try: port = int(self.txtMapserverPort.text()) except: return None local = self.radioLocalPath.isChecked() if local: folder = self.fileMapserver.filePath() else: folder = self.txtRemoteFolder.text() url = self.txtMapserverUrl.text() servicesPath = self.txtMapServicesPath.text() projFolder = self.txtProjFolder.text() server = MapserverServer(name, url, local, folder, authid, host, port, servicesPath, projFolder) return server def createGeocatLiveServer(self): name = self.txtGeocatLiveName.text() geoserverAuthid = self.geocatLiveGeoserverAuth.configId() geonetworkAuthid = self.geocatLiveGeonetworkAuth.configId() if bool(geoserverAuthid) and bool(geonetworkAuthid): userid = self.txtGeocatLiveIdentifier.text() server = GeocatLiveServer(name, userid, geoserverAuthid, geonetworkAuthid) return server def addAuthWidgets(self): self.geoserverAuth = QgsAuthConfigSelect() self.geoserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geoserverAuth) self.geoserverAuthWidget.setLayout(layout) self.geoserverAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.mapserverAuth = QgsAuthConfigSelect() self.mapserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.mapserverAuth) self.mapserverAuthWidget.setLayout(layout) self.mapserverAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.postgisAuth = QgsAuthConfigSelect() self.postgisAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.postgisAuth) self.postgisAuthWidget.setLayout(layout) self.postgisAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.cswAuth = QgsAuthConfigSelect() self.cswAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.cswAuth) self.cswAuthWidget.setLayout(layout) self.cswAuthWidget.setFixedHeight(self.txtGeoserverUrl.height()) self.geocatLiveGeoserverAuth = QgsAuthConfigSelect() self.geocatLiveGeoserverAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geocatLiveGeoserverAuth) self.geocatLiveGeoserverAuthWidget.setLayout(layout) self.geocatLiveGeoserverAuthWidget.setFixedHeight( self.txtGeoserverUrl.height()) self.geocatLiveGeonetworkAuth = QgsAuthConfigSelect() self.geocatLiveGeonetworkAuth.selectedConfigIdChanged.connect( self._setCurrentServerHasChanges) layout = QHBoxLayout() layout.setMargin(0) layout.addWidget(self.geocatLiveGeonetworkAuth) self.geocatLiveGeonetworkAuthWidget.setLayout(layout) self.geocatLiveGeonetworkAuthWidget.setFixedHeight( self.txtGeoserverUrl.height()) def addMenuToButtonNew(self): menu = QMenu() menu.addAction("GeoServer", lambda: self._addServer("GeoServer", GeoserverServer)) menu.addAction("MapServer", lambda: self._addServer("MapServer", MapserverServer)) menu.addAction( "GeoCat Live", lambda: self._addServer("GeoCat Live", GeocatLiveServer)) menu.addAction("GeoNetwork", lambda: self._addServer("GeoNetwork", GeonetworkServer)) #menu.addAction("CSW", lambda: self._addServer("CSW", CswServer)) menu.addAction("PostGIS", lambda: self._addServer("PostGIS", PostgisServer)) self.buttonNew.setMenu(menu) def buttonRemoveClicked(self): item = self.listServers.currentItem() if item is None: return name = self.listServers.itemWidget(item).serverName() removeServer(name) self.listServers.takeItem(self.listServers.currentRow()) self.listServers.setCurrentItem(None) self.checkServersHaveBeenDefined() def populateServers(self): self.listServers.clear() servers = allServers().values() for server in servers: self.addServerItem(server) self.checkServersHaveBeenDefined() def addServerItem(self, server): widget = ServerItemWidget(server) item = QListWidgetItem(self.listServers) item.setSizeHint(widget.sizeHint()) self.listServers.addItem(item) self.listServers.setItemWidget(item, widget) self.checkServersHaveBeenDefined() return item def _addServer(self, name, clazz): if self.currentServerHasChanges: self.bar.pushMessage( self.tr("Save changes to current server before creating one"), level=Qgis.Warning, duration=5) else: name = self.getNewName(name) server = clazz(name) addServer(server) self.setCurrentServer(server) item = self.addServerItem(server) self.listServers.setCurrentItem(item) def populatePostgisComboWithPostgisServers(self): self.comboGeoserverDatabase.clear() servers = allServers().values() for s in servers: if isinstance(s, PostgisServer): self.comboGeoserverDatabase.addItem(s.name) def populatePostgisComboWithGeoserverPostgisServers(self): url = self.txtGeoserverUrl.text().strip() self.comboGeoserverDatabase.clear() server = self.createGeoserverServer() if server is None: self.bar.pushMessage(self.tr("Wrong values in server definition"), level=Qgis.Warning, duration=5) return try: datastores = execute(server.postgisDatastores) except: datastores = [] if datastores: self.comboGeoserverDatabase.addItems(datastores) else: self.bar.pushMessage(self.tr( "No PostGIS datastores in server or could not retrieve them"), level=Qgis.Warning, duration=5) def _setCurrentServerHasChanges(self): self.currentServerHasChanges = True def setCurrentServer(self, server): self.currentServer = server if server is None: self.stackedWidget.setCurrentWidget(self.widgetEmpty) elif isinstance(server, GeoserverServer): self.stackedWidget.setCurrentWidget(self.widgetGeoserver) self.txtGeoserverName.setText(server.name) self.txtGeoserverUrl.setText(server.url) self.geoserverAuth.setConfigId(server.authid) self.comboGeoserverDataStorage.blockSignals(True) self.comboGeoserverDataStorage.setCurrentIndex(server.storage) self.geoserverDatastorageChanged() if server.postgisdb is not None: self.comboGeoserverDatabase.setCurrentText(server.postgisdb) self.chkUseOriginalDataSource.setChecked( server.useOriginalDataSource) self.comboGeoserverDataStorage.blockSignals(False) elif isinstance(server, MapserverServer): self.stackedWidget.setCurrentWidget(self.widgetMapserver) self.txtMapserverName.setText(server.name) self.fileMapserver.setFilePath(server.folder) self.txtRemoteFolder.setText(server.folder) self.txtMapserverHost.setText(server.host) self.txtMapserverPort.setText(str(server.port)) self.mapserverAuth.setConfigId(server.authid) self.txtMapserverUrl.setText(server.url) self.txtMapServicesPath.setText(server.servicesPath) self.txtProjFolder.setText(server.projFolder) self.radioLocalPath.setChecked(server.useLocalFolder) self.radioFtp.setChecked(not server.useLocalFolder) self.mapserverStorageChanged(server.useLocalFolder) elif isinstance(server, PostgisServer): self.stackedWidget.setCurrentWidget(self.widgetPostgis) self.txtPostgisName.setText(server.name) self.txtPostgisDatabase.setText(server.database) self.txtPostgisPort.setText(server.port) self.txtPostgisServerAddress.setText(server.host) self.txtPostgisSchema.setText(server.schema) self.postgisAuth.setConfigId(server.authid) elif isinstance(server, (GeonetworkServer, CswServer)): self.stackedWidget.setCurrentWidget(self.widgetMetadataCatalog) self.txtCswName.setText(server.name) self.txtCswNode.setText(server.node) self.txtCswUrl.setText(server.url) self.cswAuth.setConfigId(server.authid) self.comboMetadataProfile.setCurrentIndex(server.profile) elif isinstance(server, GeocatLiveServer): self.stackedWidget.setCurrentWidget(self.widgetGeocatLive) self.txtGeocatLiveName.setText(server.name) self.txtGeocatLiveIdentifier.setText(server.userid) self.geocatLiveGeoserverAuth.setConfigId(server.geoserverAuthid) self.geocatLiveGeonetworkAuth.setConfigId(server.geonetworkAuthid) self.currentServerHasChanges = False def getNewName(self, name): servers = list(allServers().keys()) i = 1 while True: n = name + str(i) if n not in servers: return n else: i += 1 def saveButtonClicked(self): if self.saveCurrentServer(): self.currentServerHasChanges = False else: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong values in current item"), level=Qgis.Warning, duration=5) def canClose(self): if self.currentServerHasChanges: res = QMessageBox.question( self, self.tr("Servers"), self. tr("Do you want to close without saving the current changes?"), QMessageBox.Cancel | QMessageBox.No | QMessageBox.Yes, QMessageBox.Yes) return res == QMessageBox.Yes else: return True
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) iconSize = settings.value("iconsize", 24) self.mToolbar.setIconSize(QSize(iconSize, iconSize)) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save?'), self.tr('There are unsaved changes in this model, do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=QgsMessageBar.WARNING, duration=5) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('SVG files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.alg.name) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=QgsMessageBar.SUCCESS, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) search_strings = text.split(' ') allAlgs = algList.algs for providerName in list(allAlgs.keys()): name = 'ACTIVATE_' + providerName.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[providerName].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue item_text = [alg.name.lower()] item_text.extend(alg.tags.split(',')) show = not search_strings or all( any(part in t for t in item_text) for part in search_strings) if show: if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = algList.getProviderFromName(providerName) providerItem.setText(0, provider.getDescription()) providerItem.setToolTip(0, provider.getDescription()) providerItem.setIcon(0, provider.getIcon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class MissingSuppliesBaseDialog(QDialog, DIALOG_LOG_EXCEL_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not def __init__(self, db, conn_manager, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self._db = db self.conn_manager = conn_manager self.parent = parent self.logger = Logger() self.app = AppInterface() self.validators = Validators() self.names = self._db.names self._dialog_mode = None self._running_tool = False self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings self.tool_name = "" self.gdb_layer_paths = dict() self.alphanumeric_file_paths = dict() self.spreadsheet_structure = 'TABLA_RESUMEN OMISIONES_TERRENO COMISIONES_TERRENO OMISIONES_MEJORAS COMISIONES_MEJORAS OMISIONES_UNID_PH COMISIONES_UNID_PH OMISIONES_MZ COMISIONES_MZ OMISIONES_VR COMISIONES_VR' # Initialize self.initialize_feedback() # Set MessageBar for QDialog self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.button(QDialogButtonBox.Ok).setText( QCoreApplication.translate("MissingSuppliesBaseDialog", "Import")) self.finished.connect(self.finished_slot) self.btn_browse_file_predio.clicked.connect( make_file_selector( self.txt_file_path_predio, QCoreApplication.translate( "MissingSuppliesBaseDialog", "Select the Predio .csv file with SNC data "), QCoreApplication.translate("MissingSuppliesBaseDialog", 'CSV File (*.csv)'))) self.btn_browse_file_uni.clicked.connect( make_file_selector( self.txt_file_path_uni, QCoreApplication.translate( "MissingSuppliesBaseDialog", "Select the UNI .lis file with Cobol data "), QCoreApplication.translate("MissingSuppliesBaseDialog", 'lis File (*.lis)'))) self.btn_browse_file_gdb.clicked.connect( make_folder_selector(self.txt_file_path_gdb, title=QCoreApplication.translate( 'MissingSuppliesBaseDialog', 'Open GDB folder'), parent=None)) self.btn_browse_file_folder_supplies.clicked.connect( make_folder_selector(self.txt_file_path_folder_supplies, title=QCoreApplication.translate( "MissingCobolSuppliesDialog", "Select folder to save data"), parent=None)) # Set validations file_validator_predio = FileValidator(pattern='*.csv', allow_non_existing=False) file_validator_lis = FileValidator(pattern='*.lis', allow_non_existing=False) dir_validator_gdb = DirValidator(pattern='*.gdb', allow_non_existing=False) dir_validator_folder = DirValidator(pattern=None, allow_empty_dir=True) non_empty_validator_name = NonEmptyStringValidator() self.txt_file_path_predio.setValidator(file_validator_predio) self.txt_file_path_uni.setValidator(file_validator_lis) self.txt_file_path_gdb.setValidator(dir_validator_gdb) self.txt_file_path_folder_supplies.setValidator(dir_validator_folder) self.txt_file_names_supplies.setValidator(non_empty_validator_name) self.txt_file_path_predio.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_uni.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_gdb.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_folder_supplies.textChanged.connect( self.validators.validate_line_edits) self.txt_file_names_supplies.textChanged.connect( self.validators.validate_line_edits) self.txt_file_path_predio.textChanged.connect(self.input_data_changed) self.txt_file_path_uni.textChanged.connect(self.input_data_changed) self.txt_file_path_gdb.textChanged.connect(self.input_data_changed) self.txt_file_path_folder_supplies.textChanged.connect( self.input_data_changed) self.txt_file_names_supplies.textChanged.connect( self.input_data_changed) def progress_configuration(self, base, num_process): """ :param base: Where to start counting from :param num_process: Number of steps """ self.progress_base = base self.progress_maximum = 100 * num_process self.progress.setMaximum(self.progress_maximum) def progress_changed(self): QCoreApplication.processEvents() # Listen to cancel from the user self.progress.setValue(self.progress_base + self.custom_feedback.progress()) def reject(self): if self._running_tool: reply = QMessageBox.question( self, QCoreApplication.translate("MissingSuppliesBaseDialog", "Warning"), QCoreApplication.translate( "MissingSuppliesBaseDialog", "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database." ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.custom_feedback.cancel() self._running_tool = False msg = QCoreApplication.translate( "MissingSuppliesBaseDialog", "The '{}' tool was cancelled.").format(self.tool_name) self.logger.info(__name__, msg) self.show_message(msg, Qgis.Info) else: if self._db_was_changed: self.conn_manager.db_connection_changed.emit( self._db, self._db.test_connection()[0], self.db_source) self.logger.info(__name__, "Dialog closed.") self.done(1) def finished_slot(self, result): self.bar.clearWidgets() def input_data_changed(self): self.set_import_button_enabled(self.validate_inputs()) def set_import_button_enabled(self, enable): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) def validate_inputs(self): raise NotImplementedError def validate_common_inputs(self): return self.txt_file_path_gdb.validator().validate( self.txt_file_path_gdb.text().strip(), 0)[0] == QValidator.Acceptable def initialize_feedback(self): self.progress.setValue(0) self.progress.setVisible(False) self.custom_feedback = CustomFeedback() self.custom_feedback.progressChanged.connect(self.progress_changed) self.set_gui_controls_enabled(True) def set_gui_controls_enabled(self, enable): self.gbx_data_source.setEnabled(enable) if self.buttonBox.button( QDialogButtonBox.Ok ) is not None: # It's None if the tool finished successfully self.set_import_button_enabled(enable) def db_connection_changed(self, db, ladm_col_db, db_source): # We dismiss parameters here, after all, we already have the db, and the ladm_col_db may change from this moment # until we close the supplies dialog (e.g., we might run an import schema before under the hood) self._db_was_changed = True def save_settings(self, system): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/predio_path'.format( system), self.txt_file_path_predio.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/uni_path'.format(system), self.txt_file_path_uni.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/gdb_path'.format(system), self.txt_file_path_gdb.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/folder_path'.format( system), self.txt_file_path_folder_supplies.text()) settings.setValue( 'Asistente-LADM-COL/missing_supplies_{}/file_names'.format(system), self.txt_file_names_supplies.text()) def restore_settings(self, system): settings = QSettings() self.txt_file_path_predio.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/predio_path'.format( system), '')) self.txt_file_path_uni.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/uni_path'.format( system), '')) self.txt_file_path_gdb.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/gdb_path'.format( system), '')) self.txt_file_path_folder_supplies.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/folder_path'.format( system), '')) self.txt_file_names_supplies.setText( settings.value( 'Asistente-LADM-COL/missing_supplies_{}/file_names'.format( system), '')) def load_lis_files(self, alphanumeric_file_paths): root = QgsProject.instance().layerTreeRoot() alphanumeric_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "LIS Supplies")) for name in alphanumeric_file_paths: uri = 'file:///{}?type=csv&delimiter=;&detectTypes=yes&geomType=none&subsetIndex=no&watchFile=no'.format( normalize_local_url(alphanumeric_file_paths[name])) layer = QgsVectorLayer(uri, name, 'delimitedtext') if layer.isValid(): self.alphanumeric_file_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) alphanumeric_group.addLayer(layer) else: return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the LIS file called '{}'.". format(name)) return True, '' def load_csv_files(self, alphanumeric_file_paths): root = QgsProject.instance().layerTreeRoot() alphanumeric_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "SNC Alphanumeric Supplies")) for name in alphanumeric_file_paths: layer = QgsVectorLayer(alphanumeric_file_paths[name], name, 'ogr') if layer.isValid(): self.alphanumeric_file_paths[name] = layer QgsProject.instance().addMapLayer(layer, False) alphanumeric_group.addLayer(layer) else: return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the CSV file called '{}'.". format(name)) return True, '' def load_gdb_files(self, required_layers): gdb_path = self.txt_file_path_gdb.text() layer = QgsVectorLayer(gdb_path, 'layer name', 'ogr') if not layer.isValid(): return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "There were troubles loading the GDB.") sublayers = layer.dataProvider().subLayers() root = QgsProject.instance().layerTreeRoot() gdb_group = root.addGroup( QCoreApplication.translate("MissingSuppliesBaseDialog", "GDB Supplies")) for data in sublayers: sublayer = data.split('!!::!!')[1] if sublayer in required_layers: layer = QgsVectorLayer(gdb_path + '|layername=' + sublayer, sublayer, 'ogr') self.gdb_layer_paths[sublayer] = layer QgsProject.instance().addMapLayer(layer, False) gdb_group.addLayer(layer) if len(self.gdb_layer_paths) != len(required_layers): return False, QCoreApplication.translate( "MissingSuppliesBaseDialog", "The GDB does not have the required layers!") return True, '' def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 15)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) tooltips = self._alg.getParameterDescriptions() self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.isAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') if param.optional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name] = label wrapper = param.wrapper(self) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name] = widget if param.name in list(tooltips.keys()): tooltip = tooltips[param.name] else: tooltip = param.description label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.isAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.hidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.tabWidget = QTabWidget() self.tabWidget.setMinimumWidth(300) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.txtHelp = QTextBrowser() html = None isText, algHelp = self._alg.help() if algHelp is not None: algHelp = algHelp if isText else QUrl(algHelp) try: if isText: self.txtHelp.setHtml(algHelp) else: html = self.tr('<p>Downloading algorithm help... Please wait.</p>') self.txtHelp.setHtml(html) self.tabWidget.addTab(self.txtHelp, 'Help') self.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp)) self.reply.finished.connect(self.requestFinished) except: pass self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def requestFinished(self): """Change the webview HTML content""" reply = self.sender() if reply.error() != QNetworkReply.NoError: html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString())) else: html = str(reply.readAll()) reply.deleteLater() self.txtHelp.setHtml(html) def getAvailableDependencies(self): # spellok if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in list(self.model.algs.values()): if alg.modeler_name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.isAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): # upgrade paramType to list if type(paramType) is not list: paramType = [paramType] values = [] inputs = self.model.inputs for i in list(inputs.values()): param = i.param for t in paramType: if isinstance(param, t): if dataType is not None: if param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) break if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in list(self.model.algs.values()): if alg.modeler_name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.modeler_name, out.name)) else: values.append(ValueFromOutput(alg.modeler_name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description else: alg = self.model.algs[value.alg] return self.tr("'{0}' from algorithm '{1}'").format(alg.algorithm.getOutputFromName(value.output).description, alg.description) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameters: if param.hidden: continue if param.name in alg.params: value = alg.params[param.name] else: value = param.default self.wrappers[param.name].setValue(value) for name, out in list(alg.outputs.items()): self.valueItems[name].setText(out.description) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.id()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameters outputs = self._alg.outputs for param in params: if param.hidden: continue if not self.setParamValue(alg, param, self.wrappers[param.name]): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description, level=QgsMessageBar.WARNING) return None for output in outputs: if not output.hidden: name = str(self.valueItems[output.name].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok for selected in selectedOptions: alg.dependencies.append(availableDependencies[selected].name) # spellok self._alg.processBeforeAddingToModeler(alg, self.model) return alg def setParamValue(self, alg, param, wrapper): try: if wrapper.widget: value = wrapper.value() alg.params[param.name] = value return True except InvalidParameterValue: return False def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close()
class DialogExportData(QDialog, DIALOG_UI): ValidExtensions = ['xtf', 'itf', 'gml', 'xml'] current_row_schema = 0 def __init__(self, iface, db, qgis_utils): QDialog.__init__(self) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.db = db self.qgis_utils = qgis_utils self.base_configuration = BaseConfiguration() self.ilicache = IliCache(self.base_configuration) self.ilicache.refresh() self.type_combo_box.clear() self.type_combo_box.addItem( QCoreApplication.translate("DialogExportData", "PostgreSQL/PostGIS"), 'pg') self.type_combo_box.addItem( QCoreApplication.translate("DialogExportData", "GeoPackage"), 'gpkg') self.type_combo_box.currentIndexChanged.connect(self.type_changed) self.type_changed() self.xtf_file_browse_button.clicked.connect( make_save_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File"), file_filter=QCoreApplication.translate( "DialogExportData", "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)" ), extension='.xtf', extensions=['.' + ext for ext in self.ValidExtensions])) self.xtf_file_browse_button.clicked.connect( self.xtf_browser_opened_to_true) self.xtf_browser_was_opened = False self.validators = Validators() fileValidator = FileValidator( pattern=['*.' + ext for ext in self.ValidExtensions], allow_non_existing=True) self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogExportData", "[Name of the XTF to be created]")) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.xtf_file_line_edit.textChanged.connect( self.xtf_browser_opened_to_false) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # PG self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self.db_connect_label.setText(self.db.dict_conn_params["database"]) self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogExportData", "Connection Settings")) # GPKG self.gpkg_file_line_edit.setPlaceholderText( QCoreApplication.translate( "DialogExportData", "[Name of the Geopackage to be created]")) self.gpkg_file_browse_button.clicked.connect( make_file_selector(self.gpkg_file_line_edit, title=QCoreApplication.translate( "DialogExportData", "Open GeoPackage database file"), file_filter=QCoreApplication.translate( "DialogExportData", "GeoPackage Database (*.gpkg)"))) gpkgFileValidator = FileValidator(pattern='*.gpkg') self.gpkg_file_line_edit.setValidator(gpkgFileValidator) self.gpkg_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.gpkg_file_line_edit.textChanged.emit( self.gpkg_file_line_edit.text()) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogExportData", "Show log")) self.log_config.setFlat(True) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self.buttonBox.addButton( QCoreApplication.translate("DialogExportData", "Export data"), QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) def showEvent(self, event): # update after create dialog self.update_schema_names_model() self.update_model_names(self.get_checked_schema()) self.restore_configuration() def update_schema_names_model(self): res, msg = self.db.test_connection() schema_names = self.db._schema_names_list() if schema_names: for schema_name in schema_names: item = QListWidgetItem(schema_name['schema_name']) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) item.setCheckState(Qt.Unchecked) self.schema_names_list_widget.addItem(item) default_item = self.schema_names_list_widget.item(0) default_item.setCheckState(Qt.Checked) else: self.schema_names_list_widget.clear() self.export_models_list_view.setModel(QStandardItemModel()) message_error = 'There are no schemes to export into the database. Please select another database.' self.txtStdout.setText( QCoreApplication.translate("DialogExportData", message_error)) self.show_message(message_error, Qgis.Warning) self.schema_names_list_widget.currentRowChanged.connect( self.on_current_row_changed_schema_names) self.schema_names_list_widget.itemChanged.connect( self.on_itemchanged_schema_name) def on_itemchanged_schema_name(self, selected_item): # disconnect signal to do changes in the items self.schema_names_list_widget.itemChanged.disconnect( self.on_itemchanged_schema_name) for index in range(self.schema_names_list_widget.count()): item = self.schema_names_list_widget.item(index) item.setCheckState(Qt.Unchecked) item.setSelected(False) if item == selected_item: select_index = index item = self.schema_names_list_widget.item(select_index) item.setCheckState(Qt.Checked) item.setSelected(True) # Update list view with models name info schema_name = item.text() self.update_model_names(schema_name) # connect signal to check when the items change self.schema_names_list_widget.itemChanged.connect( self.on_itemchanged_schema_name) def on_current_row_changed_schema_names(self, current_row): for index in range(self.schema_names_list_widget.count()): item = self.schema_names_list_widget.item(index) item.setCheckState(Qt.Unchecked) item = self.schema_names_list_widget.item(current_row) if item: item.setCheckState(Qt.Checked) # Update list view with models name info schema_name = item.text() self.update_model_names(schema_name) def update_model_names(self, dbschema): self.export_models_qmodel = QStandardItemModel() db_models = None if self.type_combo_box.currentData() == 'gpkg': db_models = self.db.get_models() elif self.type_combo_box.currentData() == 'pg': db_models = self.db.get_models(dbschema) if dbschema else None if db_models: for db_model in db_models: regex = re.compile(r'(?:\{[^\}]*\}|\s)') for modelname in regex.split(db_model['modelname']): if modelname and modelname not in DEFAULT_HIDDEN_MODELS: item = QStandardItem(modelname.strip()) item.setCheckable(False) item.setEditable(False) self.export_models_qmodel.appendRow(item) self.export_models_list_view.setModel(self.export_models_qmodel) def get_checked_schema(self): checked_schema = None for index in range(self.schema_names_list_widget.count()): item = self.schema_names_list_widget.item(index) if item.checkState() == Qt.Checked: checked_schema = item.text() break return checked_schema def get_ili_models(self): ili_models = list() for index in range(self.export_models_qmodel.rowCount()): item = self.export_models_qmodel.item(index) ili_models.append(item.text()) return ili_models def show_settings(self): dlg = self.qgis_utils.get_settings_dialog() dlg.tabWidget.setCurrentIndex(SETTINGS_CONNECTION_TAB_INDEX) if dlg.exec_(): self.db = self.qgis_utils.get_db_connection() self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self.db_connect_label.setText(self.db.dict_conn_params['database']) self.update_schema_names_model() def accepted(self): configuration = self.update_configuration() if not self.get_checked_schema(): message_error = QCoreApplication.translate( "DialogExportData", "You need to select a valid schema where to get the data from." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.connection_setting_button.setFocus() return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid XTF file before exporting data.") self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid schema to export. This schema does not have information to export." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.export_models_list_view.setFocus() return if not configuration.iliexportmodels: message_error = QCoreApplication.translate( "DialogExportData", "Please set a model before exporting data.") self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.export_models_list_view.setFocus() return if self.type_combo_box.currentData() == 'gpkg': if not configuration.dbfile or self.gpkg_file_line_edit.validator( ).validate(configuration.dbfile, 0)[0] != QValidator.Acceptable: message_error = QCoreApplication.translate( "DialogExportData", "Please set an existing database file before creating the project." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.gpkg_file_line_edit.setFocus() return # If xtf browser was opened and the file exists, the user already chose # to overwrite the file if os.path.isfile(self.xtf_file_line_edit.text().strip() ) and not self.xtf_browser_was_opened: self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Warning) self.msg.setText( QCoreApplication.translate( "DialogExportData", "{filename} already exists.\nDo you want to replace it?"). format(filename=os.path.basename( self.xtf_file_line_edit.text().strip()))) self.msg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box = self.msg.exec_() if msg_box == QMessageBox.No: return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() exporter = iliexporter.Exporter() exporter.tool_name = 'ili2pg' if self.type_combo_box.currentData( ) == 'pg' else 'ili2gpkg' exporter.configuration = configuration self.save_configuration(configuration) exporter.stdout.connect(self.print_info) exporter.stderr.connect(self.on_stderr) exporter.process_started.connect(self.on_process_started) exporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if exporter.run() != iliexporter.Exporter.SUCCESS: self.enable() self.progress_bar.hide() self.show_message( QCoreApplication.translate( "DialogExportData", "An error occurred when exporting the data. For more information see the log..." ), Qgis.Warning) return except JavaNotFoundError: # Set JAVA PATH get_java_path_dlg = DialogGetJavaPath() get_java_path_dlg.setModal(True) if get_java_path_dlg.exec_(): configuration = self.update_configuration() if not get_java_path_from_qgis_model_baker(): message_error_java = QCoreApplication.translate( "DialogExportData", """Java could not be found. You can configure the JAVA_HOME environment variable, restart QGIS and try again.""" ) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_error_java) self.show_message(message_error_java, Qgis.Warning) self.enable() self.progress_bar.hide() return self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.show_message( QCoreApplication.translate( "DialogExportData", "Export of the data was successfully completed."), Qgis.Success) def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export', configuration.xtffile) settings.setValue('Asistente-LADM_COL/db_connection_source', self.type_combo_box.currentData()) settings.setValue('Asistente-LADM_COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) if self.type_combo_box.currentData() == 'gpkg': settings.setValue( 'Asistente-LADM_COL/QgisModelBaker/ili2gpkg/dbfile', configuration.dbfile) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM_COL/QgisModelBaker/ili2pg/xtffile_export')) self.type_combo_box.setCurrentIndex( self.type_combo_box.findData( settings.value('Asistente-LADM_COL/db_connection_source', 'pg'))) self.type_changed() # Show log value_show_log = settings.value( 'Asistente-LADM_COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository custom_model_is_checked = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', type=bool) if custom_model_is_checked: self.custom_model_directories = settings.value( 'Asistente-LADM_COL/models/custom_models') def update_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ configuration = ExportConfiguration() if self.type_combo_box.currentData() == 'pg': # PostgreSQL specific options configuration.dbhost = self.db.dict_conn_params["host"] configuration.dbport = self.db.dict_conn_params["port"] configuration.dbusr = self.db.dict_conn_params["username"] configuration.database = self.db.dict_conn_params["database"] configuration.dbschema = self.get_checked_schema() configuration.dbpwd = self.db.dict_conn_params["password"] elif self.type_combo_box.currentData() == 'gpkg': configuration.dbfile = self.db.dict_conn_params["dbfile"] configuration.xtffile = self.xtf_file_line_edit.text().strip() java_path = get_java_path_from_qgis_model_baker() if java_path: self.base_configuration.java_path = java_path # Check custom model directories if QSettings().value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', type=bool): if self.custom_model_directories is None: self.base_configuration.custom_model_directories_enabled = False else: self.base_configuration.custom_model_directories = self.custom_model_directories self.base_configuration.custom_model_directories_enabled = True configuration.base_configuration = self.base_configuration if self.get_ili_models(): configuration.iliexportmodels = ';'.join(self.get_ili_models()) configuration.ilimodels = ';'.join(self.get_ili_models()) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append( QCoreApplication.translate("DialogExportData", "Finished ({})").format(exit_code)) if result == iliexporter.Exporter.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.enable() def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: process data...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(70) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(85) QCoreApplication.processEvents() def show_help(self): self.qgis_utils.show_help("export_data") def disable(self): self.type_combo_box.setEnabled(False) self.pg_config.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.type_combo_box.setEnabled(True) self.pg_config.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): self.bar.pushMessage("Asistente LADM_COL", message, level, duration=0) def type_changed(self): self.progress_bar.hide() if self.type_combo_box.currentData() == 'pg': self.pg_config.show() self.gpkg_config.hide() elif self.type_combo_box.currentData() == 'gpkg': self.pg_config.hide() self.gpkg_config.show() def xtf_browser_opened_to_true(self): """ Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file. """ self.xtf_browser_was_opened = True def xtf_browser_opened_to_false(self): """ Slot. Sets a flag to false to eventually ask a user whether to overwrite a file. """ self.xtf_browser_was_opened = False
class SettingsDialog(QDialog, DIALOG_UI): db_connection_changed = pyqtSignal(DBConnector, bool, str) # dbconn, ladm_col_db, source active_role_changed = pyqtSignal() def __init__(self, parent=None, qgis_utils=None, conn_manager=None): QDialog.__init__(self, parent) self.setupUi(self) self.logger = Logger() self.conn_manager = conn_manager self._db = None self.qgis_utils = qgis_utils self.db_source = COLLECTED_DB_SOURCE # default db source self._required_models = list() self._tab_pages_list = list() self.init_db_engine = None self._action_type = None self.dbs_supported = ConfigDbSupported() self.online_models_radio_button.setChecked(True) self.online_models_radio_button.toggled.connect( self.model_provider_toggle) self.custom_model_directories_line_edit.setText("") self.custom_models_dir_button.clicked.connect( self.show_custom_model_dir) self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.helpRequested.connect(self.show_help) self.finished.connect(self.finished_slot) self.btn_test_connection.clicked.connect(self.test_connection) self.btn_test_ladm_col_structure.clicked.connect( self.test_ladm_col_structure) self.btn_test_service.clicked.connect(self.test_service) self.btn_test_service_transitional_system.clicked.connect( self.test_service_transitional_system) self.btn_default_value_sources.clicked.connect( self.set_default_value_source_service) self.btn_default_value_transitional_system.clicked.connect( self.set_default_value_transitional_system_service) self.chk_use_roads.toggled.connect(self.update_images_state) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.cbo_db_engine.clear() self._lst_db = self.dbs_supported.get_db_factories() self._lst_panel = dict() for key, value in self._lst_db.items(): self.cbo_db_engine.addItem(value.get_name(), key) self._lst_panel[key] = value.get_config_panel(self) self._lst_panel[key].notify_message_requested.connect( self.show_message) self.db_layout.addWidget(self._lst_panel[key]) self.db_engine_changed() # Trigger some default behaviours self.restore_db_source_settings( ) # restore settings with default db source self.restore_settings() self.roles = Role_Registry() self.load_roles() self.cbo_db_engine.currentIndexChanged.connect(self.db_engine_changed) self.rejected.connect(self.close_dialog) def set_db_source(self, db_source): self.db_source = db_source self.restore_db_source_settings() def set_tab_pages_list(self, tab_pages_list): self._tab_pages_list = tab_pages_list self.show_tabs(tab_pages_list) def set_required_models(self, required_models): self._required_models = required_models def show_tabs(self, tab_pages_list): """ Show only those tabs that are listed in tab_pages_list, if any. If it's an empty list, show all tabs. """ if tab_pages_list: for i in reversed(range(self.tabWidget.count())): if i not in tab_pages_list: self.tabWidget.removeTab(i) def load_roles(self): """ Initialize group box for selecting the active role """ self.gbx_active_role_layout = QVBoxLayout() dict_roles = self.roles.get_roles_info() checked = False active_role = self.roles.get_active_role() # Initialize radio buttons for k, v in dict_roles.items(): radio = QRadioButton(v) radio.setToolTip(self.roles.get_role_description(k)) if not checked: if k == active_role: radio.setChecked(True) checked = True self.gbx_active_role_layout.addWidget(radio) self.gbx_active_role.setLayout(self.gbx_active_role_layout) def close_dialog(self): self.close() def showEvent(self, event): # It is necessary to reload the variables # to load the database and schema name self.restore_settings() self.btn_test_ladm_col_structure.setVisible( self._action_type != EnumDbActionType.SCHEMA_IMPORT) def model_provider_toggle(self): if self.offline_models_radio_button.isChecked(): self.custom_model_directories_line_edit.setVisible(True) self.custom_models_dir_button.setVisible(True) else: self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) self.custom_model_directories_line_edit.setText("") def _get_db_connector_from_gui(self): current_db_engine = self.cbo_db_engine.currentData() params = self._lst_panel[current_db_engine].read_connection_parameters( ) db = self._lst_db[current_db_engine].get_db_connector(params) return db def get_db_connection(self): if self._db is not None: self.logger.info(__name__, "Returning existing db connection...") else: self.logger.info(__name__, "Getting new db connection...") self._db = self._get_db_connector_from_gui() self._db.open_connection() return self._db def show_custom_model_dir(self): dlg = CustomModelDirDialog( self.custom_model_directories_line_edit.text(), self) dlg.exec_() def accepted(self): """ First check if connection to DB/schema is valid, if not, block the dialog. If valid, check it complies with LADM. If not, block the dialog. If it complies, we have two options: To emit db_connection changed or not. Finally, we store options in QSettings. """ ladm_col_schema = False db = self._get_db_connector_from_gui() test_level = EnumTestLevel.DB_SCHEMA if self._action_type == EnumDbActionType.SCHEMA_IMPORT: # Limit the validation (used in GeoPackage) test_level |= EnumTestLevel.SCHEMA_IMPORT res, code, msg = db.test_connection( test_level ) # No need to pass required_models, we don't test that much if res: if self._action_type != EnumDbActionType.SCHEMA_IMPORT: # Only check LADM-schema if we are not in an SCHEMA IMPORT. # We know in an SCHEMA IMPORT, at this point the schema is still not LADM. ladm_col_schema, code, msg = db.test_connection( EnumTestLevel.LADM, required_models=self._required_models) if not ladm_col_schema and self._action_type != EnumDbActionType.SCHEMA_IMPORT: self.show_message(msg, Qgis.Warning) return # Do not close the dialog else: self.show_message(msg, Qgis.Warning) return # Do not close the dialog # Connection is valid and complies with LADM current_db_engine = self.cbo_db_engine.currentData() if self._lst_panel[current_db_engine].state_changed( ) or self.init_db_engine != current_db_engine: # Emit db_connection_changed if self._db is not None: self._db.close_connection() self._db = db # Update db connect with new db conn self.conn_manager.set_db_connector_for_source( self._db, self.db_source) # Emmit signal when db source changes self.db_connection_changed.emit(self._db, ladm_col_schema, self.db_source) self.logger.debug( __name__, "Settings dialog emitted a db_connection_changed.") # Save settings from tabs other than database connection self.save_settings() QDialog.accept(self) # If active role changed, refresh the GUI selected_role = self.get_selected_role() if self.roles.get_active_role() != selected_role: self.logger.info( __name__, "The active role has changed from '{}' to '{}'.".format( self.roles.get_active_role(), selected_role)) self.roles.set_active_role(selected_role) self.active_role_changed.emit() self.close() def get_selected_role(self): selected_role = None radio_checked = None for i in range(self.gbx_active_role_layout.count()): radio = self.gbx_active_role_layout.itemAt(i).widget() if radio.isChecked(): radio_checked = radio.text() break for k, v in self.roles.get_roles_info().items(): if v == radio_checked: selected_role = k break return selected_role # Role key def reject(self): self.done(0) def finished_slot(self, result): self.bar.clearWidgets() def save_settings(self): settings = QSettings() current_db_engine = self.cbo_db_engine.currentData() settings.setValue( 'Asistente-LADM_COL/db/{db_source}/db_connection_engine'.format( db_source=self.db_source), current_db_engine) dict_conn = self._lst_panel[ current_db_engine].read_connection_parameters() self._lst_db[current_db_engine].save_parameters_conn( dict_conn=dict_conn, db_source=self.db_source) settings.setValue( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', self.offline_models_radio_button.isChecked()) if self.offline_models_radio_button.isChecked(): settings.setValue('Asistente-LADM_COL/models/custom_models', self.custom_model_directories_line_edit.text()) settings.setValue('Asistente-LADM_COL/quality/use_roads', self.chk_use_roads.isChecked()) settings.setValue( 'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode', self.chk_automatic_values_in_batch_mode.isChecked()) settings.setValue('Asistente-LADM_COL/sources/document_repository', self.connection_box.isChecked()) settings.setValue( 'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting', self.chk_validate_data_importing_exporting.isChecked()) endpoint_transitional_system = self.txt_service_transitional_system.text( ).strip() settings.setValue( 'Asistente-LADM_COL/sources/service_transitional_system', (endpoint_transitional_system[:-1] if endpoint_transitional_system.endswith('/') else endpoint_transitional_system) or TransitionalSystemConfig().ST_DEFAULT_DOMAIN) endpoint = self.txt_service_endpoint.text().strip() settings.setValue( 'Asistente-LADM_COL/sources/service_endpoint', (endpoint[:-1] if endpoint.endswith('/') else endpoint) or DEFAULT_ENDPOINT_SOURCE_SERVICE) # Changes in automatic namespace or local_id configuration? current_namespace_enabled = settings.value( 'Asistente-LADM_COL/automatic_values/namespace_enabled', True, bool) current_namespace_prefix = settings.value( 'Asistente-LADM_COL/automatic_values/namespace_prefix', "") current_local_id_enabled = settings.value( 'Asistente-LADM_COL/automatic_values/local_id_enabled', True, bool) settings.setValue( 'Asistente-LADM_COL/automatic_values/namespace_enabled', self.namespace_collapsible_group_box.isChecked()) if self.namespace_collapsible_group_box.isChecked(): settings.setValue( 'Asistente-LADM_COL/automatic_values/namespace_prefix', self.txt_namespace.text()) settings.setValue( 'Asistente-LADM_COL/automatic_values/local_id_enabled', self.chk_local_id.isChecked()) if current_namespace_enabled != self.namespace_collapsible_group_box.isChecked() or \ current_namespace_prefix != self.txt_namespace.text() or \ current_local_id_enabled != self.chk_local_id.isChecked(): if self._db is not None: self.qgis_utils.automatic_namespace_local_id_configuration_changed( self._db) def restore_db_source_settings(self): settings = QSettings() default_db_engine = self.dbs_supported.id_default_db self.init_db_engine = settings.value( 'Asistente-LADM_COL/db/{db_source}/db_connection_engine'.format( db_source=self.db_source), default_db_engine) index_db_engine = self.cbo_db_engine.findData(self.init_db_engine) if index_db_engine == -1: index_db_engine = self.cbo_db_engine.findData(default_db_engine) self.cbo_db_engine.setCurrentIndex(index_db_engine) self.db_engine_changed() # restore db settings for all panels for db_engine, db_factory in self._lst_db.items(): dict_conn = db_factory.get_parameters_conn(self.db_source) self._lst_panel[db_engine].write_connection_parameters(dict_conn) self._lst_panel[db_engine].save_state() def restore_settings(self): # Restore QSettings settings = QSettings() custom_model_directories_is_checked = settings.value( 'Asistente-LADM_COL/models/custom_model_directories_is_checked', DEFAULT_USE_CUSTOM_MODELS, type=bool) if custom_model_directories_is_checked: self.offline_models_radio_button.setChecked(True) self.custom_model_directories_line_edit.setText( settings.value('Asistente-LADM_COL/models/custom_models', DEFAULT_MODELS_DIR)) self.custom_model_directories_line_edit.setVisible(True) self.custom_models_dir_button.setVisible(True) else: self.online_models_radio_button.setChecked(True) self.custom_model_directories_line_edit.setText("") self.custom_model_directories_line_edit.setVisible(False) self.custom_models_dir_button.setVisible(False) use_roads = settings.value('Asistente-LADM_COL/quality/use_roads', True, bool) self.chk_use_roads.setChecked(use_roads) self.update_images_state(use_roads) self.chk_automatic_values_in_batch_mode.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/automatic_values_in_batch_mode', True, bool)) self.connection_box.setChecked( settings.value('Asistente-LADM_COL/sources/document_repository', True, bool)) self.namespace_collapsible_group_box.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/namespace_enabled', True, bool)) self.chk_local_id.setChecked( settings.value( 'Asistente-LADM_COL/automatic_values/local_id_enabled', True, bool)) self.txt_namespace.setText( str( settings.value( 'Asistente-LADM_COL/automatic_values/namespace_prefix', ""))) self.chk_validate_data_importing_exporting.setChecked( settings.value( 'Asistente-LADM_COL/advanced_settings/validate_data_importing_exporting', True, bool)) self.txt_service_transitional_system.setText( settings.value( 'Asistente-LADM_COL/sources/service_transitional_system', TransitionalSystemConfig().ST_DEFAULT_DOMAIN)) self.txt_service_endpoint.setText( settings.value('Asistente-LADM_COL/sources/service_endpoint', DEFAULT_ENDPOINT_SOURCE_SERVICE)) def db_engine_changed(self): if self._db is not None: self._db.close_connection() self._db = None # Reset db connection for key, value in self._lst_panel.items(): value.setVisible(False) current_db_engine = self.cbo_db_engine.currentData() self._lst_panel[current_db_engine].setVisible(True) def test_connection(self): db = self._get_db_connector_from_gui() test_level = EnumTestLevel.DB_SCHEMA if self._action_type == EnumDbActionType.SCHEMA_IMPORT: test_level |= EnumTestLevel.SCHEMA_IMPORT res, code, msg = db.test_connection( test_level ) # No need to pass required_models, we don't test that much if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.logger.info(__name__, "Test connection!") self.logger.debug( __name__, "Test connection ({}): {}, {}".format(test_level, res, msg)) def test_ladm_col_structure(self): db = self._get_db_connector_from_gui() res, code, msg = db.test_connection( test_level=EnumTestLevel.LADM, required_models=self._required_models) if db is not None: db.close_connection() self.show_message(msg, Qgis.Info if res else Qgis.Warning) self.logger.info(__name__, "Test LADM structure!") self.logger.debug( __name__, "Test connection ({}): {}, {}".format(EnumTestLevel.LADM, res, msg)) def test_service(self): self.setEnabled(False) QCoreApplication.processEvents() res, msg = self.qgis_utils.is_source_service_valid( self.txt_service_endpoint.text().strip()) self.setEnabled(True) self.show_message(msg['text'], msg['level']) def test_service_transitional_system(self): self.setEnabled(False) QCoreApplication.processEvents() res, msg = self.qgis_utils.is_transitional_system_service_valid( self.txt_service_transitional_system.text().strip()) self.setEnabled(True) self.show_message(msg['text'], msg['level']) def set_default_value_source_service(self): self.txt_service_endpoint.setText(DEFAULT_ENDPOINT_SOURCE_SERVICE) def set_default_value_transitional_system_service(self): self.txt_service_transitional_system.setText( TransitionalSystemConfig().ST_DEFAULT_DOMAIN) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 10) def update_images_state(self, checked): self.img_with_roads.setEnabled(checked) self.img_with_roads.setToolTip( QCoreApplication.translate( "SettingsDialog", "Missing roads will be marked as errors." ) if checked else '') self.img_without_roads.setEnabled(not checked) self.img_without_roads.setToolTip( '' if checked else QCoreApplication.translate( "SettingsDialog", "Missing roads will not be marked as errors." )) def show_help(self): self.qgis_utils.show_help("settings") def set_action_type(self, action_type): self._action_type = action_type for key, value in self._lst_panel.items(): value.set_action(action_type)
class ImportDataDialog(QDialog, DIALOG_UI): ValidExtensions = [ 'xtf', 'XTF', 'itf', 'ITF', 'pdf', 'PDF', 'xml', 'XML', 'xls', 'XLS', 'xlsx', 'XLSX' ] ModelMissingRegExp = re.compile(r'Error: failed to query .*\.t_ili2db_seq') def __init__(self, iface, base_config, parent=None): QDialog.__init__(self, parent) self.iface = iface self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.db_simple_factory = DbSimpleFactory() self.buttonBox.accepted.disconnect() self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.help_requested) self.import_text = self.tr('Import Data') self.set_button_to_import_action = QAction(self.import_text, None) self.set_button_to_import_action.triggered.connect( self.set_button_to_import) self.import_without_validation_text = self.tr( 'Import without validation') self.set_button_to_import_without_validation_action = QAction( self.import_without_validation_text, None) self.set_button_to_import_without_validation_action.triggered.connect( self.set_button_to_import_without_validation) self.edit_command_action = QAction(self.tr('Edit ili2db command'), None) self.edit_command_action.triggered.connect(self.edit_command) self.import_tool_button.addAction( self.set_button_to_import_without_validation_action) self.import_tool_button.addAction(self.edit_command_action) self.import_tool_button.setText(self.import_text) self.import_tool_button.clicked.connect(self.accepted) self.xtf_file_browse_button.clicked.connect( make_file_selector( self.xtf_file_line_edit, title=self.tr('Open Transfer or Catalog File'), file_filter=self. tr('Transfer File (*.xtf *.itf *.XTF *.ITF);;Catalogue File (*.xml *.XML *.xls *.XLS *.xlsx *.XLSX)' ))) self.type_combo_box.clear() self._lst_panel = dict() for db_id in self.db_simple_factory.get_db_list(False): self.type_combo_box.addItem(displayDbIliMode[db_id], db_id) db_factory = self.db_simple_factory.create_factory(db_id) item_panel = db_factory.get_config_panel(self, DbActionType.IMPORT_DATA) self._lst_panel[db_id] = item_panel self.db_layout.addWidget(item_panel) self.type_combo_box.currentIndexChanged.connect(self.type_changed) self.ili2db_options = Ili2dbOptionsDialog() self.ili2db_options_button.clicked.connect(self.ili2db_options.open) self.ili2db_options.finished.connect(self.fill_toml_file_info_label) self.multiple_models_dialog = MultipleModelsDialog(self) self.multiple_models_button.clicked.connect( self.multiple_models_dialog.open) self.multiple_models_dialog.accepted.connect( self.fill_models_line_edit) self.validate_data = True # validates imported data by default, We use --disableValidation when is False self.base_configuration = base_config self.restore_configuration() self.validators = Validators() fileValidator = FileValidator( pattern=['*.' + ext for ext in self.ValidExtensions]) self.xtf_file_line_edit.setValidator(fileValidator) self.ili_models_line_edit.setPlaceholderText( self.tr('[Search model in repository]')) self.ili_models_line_edit.textChanged.connect( self.complete_models_completer) self.ili_models_line_edit.punched.connect( self.complete_models_completer) self.xtf_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) # Reset to import as default text self.xtf_file_line_edit.textChanged.connect(self.set_button_to_import) settings = QSettings() ilifile = settings.value('QgisModelBaker/ili2db/ilifile') self.ilicache = IliCache(base_config, ilifile or None) self.update_models_completer() self.ilicache.refresh() self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.txtStdout.setLayout(QGridLayout()) self.txtStdout.layout().setContentsMargins(0, 0, 0, 0) self.txtStdout.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def set_button_to_import(self): """ Changes the text of the button to import (with validation) and sets the validate_data to true. So on clicking the button the import will start with validation. The buttons actions are changed to be able to switch the without-validation mode. """ self.validate_data = True self.import_tool_button.removeAction(self.set_button_to_import_action) self.import_tool_button.removeAction(self.edit_command_action) self.import_tool_button.addAction( self.set_button_to_import_without_validation_action) self.import_tool_button.addAction(self.edit_command_action) self.import_tool_button.setText(self.import_text) def set_button_to_import_without_validation(self): """ Changes the text of the button to import without validation and sets the validate_data to false. So on clicking the button the import will start without validation. The buttons actions are changed to be able to switch the with-validation mode. """ self.validate_data = False self.import_tool_button.removeAction( self.set_button_to_import_without_validation_action) self.import_tool_button.removeAction(self.edit_command_action) self.import_tool_button.addAction(self.set_button_to_import_action) self.import_tool_button.addAction(self.edit_command_action) self.import_tool_button.setText(self.import_without_validation_text) def edit_command(self): """ A dialog opens giving the user the possibility to edit the ili2db command used for the import """ importer = iliimporter.Importer() importer.tool = self.type_combo_box.currentData() importer.configuration = self.updated_configuration() command = importer.command(True) edit_command_dialog = EditCommandDialog(self) edit_command_dialog.command_edit.setPlainText(command) if edit_command_dialog.exec_(): edited_command = edit_command_dialog.command_edit.toPlainText() self.accepted(edited_command) def accepted(self, edited_command=None): configuration = self.updated_configuration() db_id = self.type_combo_box.currentData() if not edited_command: if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: self.txtStdout.setText( self. tr('Please set a valid INTERLIS transfer or catalogue file before importing data.' )) self.xtf_file_line_edit.setFocus() return res, message = self._lst_panel[db_id].is_valid() if not res: self.txtStdout.setText(message) return # create schema with superuser db_factory = self.db_simple_factory.create_factory(db_id) res, message = db_factory.pre_generate_project(configuration) if not res: self.txtStdout.setText(message) return with OverrideCursor(Qt.WaitCursor): self.progress_bar.show() self.progress_bar.setValue(0) self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() dataImporter = iliimporter.Importer(dataImport=True) dataImporter.tool = self.type_combo_box.currentData() dataImporter.configuration = configuration self.save_configuration(configuration) dataImporter.stdout.connect(self.print_info) dataImporter.stderr.connect(self.on_stderr) dataImporter.process_started.connect(self.on_process_started) dataImporter.process_finished.connect(self.on_process_finished) self.progress_bar.setValue(25) try: if dataImporter.run( edited_command) != iliimporter.Importer.SUCCESS: self.enable() self.progress_bar.hide() return except JavaNotFoundError as e: self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(e.error_string) self.enable() self.progress_bar.hide() return self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) self.refresh_layers() def refresh_layers(self): # refresh layers try: for layer in self.iface.mapCanvas().layers(): layer.dataProvider().reloadData() self.iface.layerTreeView().layerTreeModel( ).recursivelyEmitDataChanged() except AttributeError: pass def print_info(self, text, text_color='#000000'): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) match = re.match(ImportDataDialog.ModelMissingRegExp, text) if match: color_log_text( '=======================================================================', self.txtStdout) color_log_text( self. tr('It looks like the required schema for the imported data has not been generated.' ), self.txtStdout) color_log_text( self.tr('Did you generate the model in the database?'), self.txtStdout) color_log_text( self. tr('Note: the model for a catalogue may be different from the data model itself.' ), self.txtStdout) color_log_text( '=======================================================================', self.txtStdout) self.advance_progress_bar_by_text(text) def show_message(self, level, message): if level == Qgis.Warning: self.bar.pushMessage(message, Qgis.Info, 10) elif level == Qgis.Critical: self.bar.pushMessage(message, Qgis.Warning, 10) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append('Finished ({})'.format(exit_code)) if result == iliimporter.Importer.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: if self.import_without_validate(): self.set_button_to_import_without_validation() self.enable() def import_without_validate(self): """ Valid if an error occurred that prevents executing the import without validations :return: True if you can execute the import without validations, False in other case """ log = self.txtStdout.toPlainText().lower() if "no models given" in log: return False if "attribute bid missing in basket" in log: return False return True def db_ili_version(self, configuration): """ Returns the ili2db version the database has been created with or None if the database could not be detected as a ili2db database """ db_connector = self.__db_connector(configuration) if db_connector: return db_connector.ili_version() return None def updated_configuration(self): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ configuration = ili2dbconfig.ImportDataConfiguration() mode = self.type_combo_box.currentData() self._lst_panel[mode].get_fields(configuration) configuration.tool = mode configuration.xtffile = self.xtf_file_line_edit.text().strip() configuration.delete_data = self.chk_delete_data.isChecked() configuration.ilimodels = self.ili_models_line_edit.text().strip() configuration.inheritance = self.ili2db_options.inheritance_type() configuration.create_basket_col = self.ili2db_options.create_basket_col( ) configuration.create_import_tid = self.ili2db_options.create_import_tid( ) configuration.stroke_arcs = self.ili2db_options.stroke_arcs() configuration.pre_script = self.ili2db_options.pre_script() configuration.post_script = self.ili2db_options.post_script() configuration.base_configuration = self.base_configuration configuration.db_ili_version = self.db_ili_version(configuration) configuration.with_schemaimport = True db_connector = self.__db_connector(configuration) if db_connector and db_connector.db_or_schema_exists(): configuration.with_schemaimport = False if not self.validate_data: configuration.disable_validation = True return configuration def save_configuration(self, configuration): settings = QSettings() settings.setValue('QgisModelBaker/ili2pg/xtffile_import', configuration.xtffile) settings.setValue('QgisModelBaker/ili2pg/deleteData', configuration.delete_data) settings.setValue('QgisModelBaker/importtype', self.type_combo_box.currentData().name) mode = self.type_combo_box.currentData() db_factory = self.db_simple_factory.create_factory(mode) config_manager = db_factory.get_db_command_config_manager( configuration) config_manager.save_config_in_qsettings() def restore_configuration(self): settings = QSettings() self.fill_toml_file_info_label() self.xtf_file_line_edit.setText( settings.value('QgisModelBaker/ili2pg/xtffile_import')) # set chk_delete_data always to unchecked because otherwise the user could delete the data accidentally self.chk_delete_data.setChecked(False) for db_id in self.db_simple_factory.get_db_list(False): configuration = iliimporter.ImportDataConfiguration() db_factory = self.db_simple_factory.create_factory(db_id) config_manager = db_factory.get_db_command_config_manager( configuration) config_manager.load_config_from_qsettings() self._lst_panel[db_id].set_fields(configuration) mode = settings.value('QgisModelBaker/importtype') mode = DbIliMode[ mode] if mode else self.db_simple_factory.default_database mode = mode & ~DbIliMode.ili self.type_combo_box.setCurrentIndex(self.type_combo_box.findData(mode)) self.type_changed() def disable(self): self.type_combo_box.setEnabled(False) for key, value in self._lst_panel.items(): value.setEnabled(False) self.ili_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.type_combo_box.setEnabled(True) for key, value in self._lst_panel.items(): value.setEnabled(True) self.ili_config.setEnabled(True) self.buttonBox.setEnabled(True) def type_changed(self): self.txtStdout.clear() self.set_button_to_import() self.progress_bar.hide() db_id = self.type_combo_box.currentData() self.db_wrapper_group_box.setTitle(displayDbIliMode[db_id]) # Refresh panels for key, value in self._lst_panel.items(): value.interlis_mode = False is_current_panel_selected = db_id == key value.setVisible(is_current_panel_selected) if is_current_panel_selected: value._show_panel() def link_activated(self, link): if link.url() == '#configure': cfg = OptionsDialog(self.base_configuration) if cfg.exec_(): settings = QSettings() settings.beginGroup('QgisModelBaker/ili2db') self.base_configuration.save(settings) else: QDesktopServices.openUrl(link) def complete_models_completer(self): if not self.ili_models_line_edit.text(): self.ili_models_line_edit.completer().setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.ili_models_line_edit.completer().complete() else: self.ili_models_line_edit.completer().setCompletionMode( QCompleter.PopupCompletion) def update_models_completer(self): completer = QCompleter(self.ilicache.model, self.ili_models_line_edit) completer.setCaseSensitivity(Qt.CaseInsensitive) completer.setFilterMode(Qt.MatchContains) self.delegate = ModelCompleterDelegate() completer.popup().setItemDelegate(self.delegate) self.ili_models_line_edit.setCompleter(completer) self.multiple_models_dialog.models_line_edit.setCompleter(completer) def fill_models_line_edit(self): self.ili_models_line_edit.setText( self.multiple_models_dialog.get_models_string()) def fill_toml_file_info_label(self): text = None if self.ili2db_options.toml_file(): text = self.tr('Extra Model Information File: {}').format(( '…' + self.ili2db_options.toml_file( )[len(self.ili2db_options.toml_file()) - 40:]) if len(self.ili2db_options.toml_file() ) > 40 else self.ili2db_options.toml_file()) self.toml_file_info_label.setText(text) self.toml_file_info_label.setToolTip(self.ili2db_options.toml_file()) def help_requested(self): os_language = QLocale( QSettings().value('locale/userLocale')).name()[:2] if os_language in ['es', 'de']: webbrowser.open( "https://opengisch.github.io/QgisModelBaker/docs/{}/user-guide.html#import-an-interlis-transfer-file-xtf" .format(os_language)) else: webbrowser.open( "https://opengisch.github.io/QgisModelBaker/docs/user-guide.html#import-an-interlis-transfer-file-xtf" ) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: create table structure...': self.progress_bar.setValue(75) QCoreApplication.processEvents() def __db_connector(self, configuration): db_factory = self.db_simple_factory.create_factory(configuration.tool) config_manager = db_factory.get_db_command_config_manager( configuration) try: db_connector = db_factory.get_db_connector( config_manager.get_uri(configuration.db_use_super_login) or config_manager.get_uri(), configuration.dbschema) db_connector.new_message.connect(self.show_message) return db_connector except (DBConnectorError, FileNotFoundError): return None
class dataCatalog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale", "nl") locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', '{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_dataCatalogDlg() self.ui.setupUi(self) #get settings self.s = settings() self.gh = gh.geometryHelper( self.iface ) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) #vars self.firstShow = True self.wms = None self.wfs = None self.wmts = None self.wcs = None self.dl = None self.zoek = '' #datamodel self.model = QtGui.QStandardItemModel(self) self.proxyModel = QtGui.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel( self.proxyModel ) #completer self.completer = QtGui.QCompleter( self ) self.completerModel = QtGui.QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer ) self.completer.setModel(self.completerModel ) self.md = metadata.MDReader(timeout=self.s.timeout, proxyUrl=self.s.proxyUrl) #eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.addWMTSbtn.clicked.connect(self.addWMTS) self.ui.addWCSbtn.clicked.connect(self.addWCS) 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.finished.connect(self.clean) def _setModel(self, records): self.model.clear() reclist = sorted(records, key=lambda k: k['title']) for rec in reclist: title = QtGui.QStandardItem( rec['title'] ) #0 wms = QtGui.QStandardItem( rec['wms'][1] ) #1 downloadLink = QtGui.QStandardItem(rec['download']) #2 id = QtGui.QStandardItem( rec['uuid'] ) #3 abstract = QtGui.QStandardItem( rec['abstract'] ) #4 wfs = QtGui.QStandardItem( rec['wfs'][1] ) #5 wcs = QtGui.QStandardItem( rec['wcs'][1] ) #6 wmts = QtGui.QStandardItem( rec['wmts'][1] ) #7 ### wmsName = QtGui.QStandardItem( rec['wms'][0] ) #8 wfsName = QtGui.QStandardItem( rec['wfs'][0] ) #9 wcsName = QtGui.QStandardItem( rec['wcs'][0] ) #10 wmtsName = QtGui.QStandardItem( rec['wmts'][0] ) #11 self.model.appendRow([title,wms,downloadLink,id,abstract,wfs,wcs,wmts,wmsName,wfsName,wcsName,wmtsName]) #overwrite def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on( self.s.proxyUrl ) if inet: 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.keywordCbx.addItems( ['']+ keywords ) 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 else: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate("datacatalog", "Kan geen verbing maken met het internet."), level=QgsMessageBar.WARNING, duration=3) #eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() 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) ) self.wcs = self.proxyModel.data( self.proxyModel.index( row, 6) ) self.wmts= self.proxyModel.data( self.proxyModel.index( row, 7) ) ## self.wmsLr = self.proxyModel.data( self.proxyModel.index( row, 8) ) self.wfsLr = self.proxyModel.data( self.proxyModel.index( row, 9) ) self.wcsLr = self.proxyModel.data( self.proxyModel.index( row, 10) ) self.wmtsLr = self.proxyModel.data( self.proxyModel.index( row, 11) ) ## title = self.proxyModel.data( self.proxyModel.index( row, 0) ) 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/><br/> <a href='http://www.nationaalgeoregister.nl/geonetwork/srv/search/?uuid=%s'> Bekijk in Nationaal Georegister</a>""" % (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.wmts: self.ui.addWMTSbtn.setEnabled(1) else: self.ui.addWMTSbtn.setEnabled(0) if self.wcs: self.ui.addWCSbtn.setEnabled(1) else: self.ui.addWCSbtn.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) elif value == 4: self.filterModel(6) elif value == 5: self.filterModel(7) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QtCore.QRegExp("?*", QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard ) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) if self.ui.filterBox.isChecked(): 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 ='' #keyword = self.ui.keywordCbx.currentText() inspiretheme= self.ui.INSPIREthemaCbx.currentText() inspireannex= self.ui.INSPIREannexCbx.currentText() inspireServiceType= self.ui.INSPIREserviceCbx.currentText() searchResult = metadata.MDdata( self.md.searchAll( self.zoek, orgName, dataType, None, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = metadata.MDdata( self.md.searchAll( self.zoek )) except: self.bar.pushMessage("Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=3) return finally: QtGui.QApplication.restoreOverrideCursor() self.ui.countLbl.setText( "Aantal gevonden: %s" % searchResult.count ) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate("datacatalog", "Er zijn geen resultaten gevonden 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:28992' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:28992' try: lyrs = metadata.getWmsLayerNames( self.wms, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wms ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.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] url= self.wms.split('?')[0] if crs != 'EPSG:28992' or crs != 'EPSG:3857' : crs = 'EPSG:28992' wmsUrl = "url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % (url, layerName, style , crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = metadata.getWFSLayerNames( self.wfs, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wfs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, QtCore.QCoreApplication.translate("datacatalog", "WFS toevoegen"), QtCore.QCoreApplication.translate("datacatalog", "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] if self.ui.wfsBboxchk.isChecked(): extent = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([extent.xMinimum(),extent.yMinimum()], int(crs.split(":")[-1]) ) maxX, maxY = self.gh.prjPtFromMapCrs([extent.xMaximum(),extent.yMaximum()], int(crs.split(":")[-1]) ) bbox = [minX, minY, maxX, maxY] else: bbox = None ###wfsUri = "WFS:{0}|{1}".format(url,layerName) wfsUri = metadata.makeWFSuri( url, layerName, crs, bbox=bbox ) try: vlayer = QgsVectorLayer( wfsUri, layerTitle , "WFS") ###vlayer = QgsVectorLayer( wfsUri, layerTitle , "ogr") QgsMapLayerRegistry.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWMTS(self): if self.wmts == None: return try: lyrs = metadata.getWMTSlayersNames( self.wmts, self.s.proxyUrl ) except: self.bar.pushMessage("Error",'Kan niet connecteren met '+ self.wmts, level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMTS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wmts ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WMTS 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] matrix = [n[2] for n in lyrs if n[1] == layerTitle ][0] frmt = [n[3] for n in lyrs if n[1] == layerTitle ][0] srs = [n[4] for n in lyrs if n[1] == layerTitle ][0] wmtsUrl= metadata.makeWMTSuri(self.wmts , layerName, matrix, format=frmt, srsname=srs) try: rlayer = QgsRasterLayer(wmtsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWCS(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) lyrs = metadata.getWCSlayerNames( self.wcs, self.s.proxyUrl ) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return finally: QtGui.QApplication.restoreOverrideCursor() if len(lyrs) == 0: self.bar.pushMessage("WCS", QtCore.QCoreApplication.translate("datacatalog", "Kan geen lagen vinden in: %s" % self.wcs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem(self, "WCS 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] layerFormat = [n[2] for n in lyrs if n[1] == layerTitle ][0] srs = [n[3] for n in lyrs if n[1] == layerTitle ][0] axis = [n[4] for n in lyrs if n[1] == layerTitle ][0] wcsUri = metadata.makeWCSuri(self.wcs, layerName, srsname=srs, invertAxis=axis ) try: rlayer = QgsRasterLayer( wcsUri, layerTitle , "wcs") if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) if rlayer.bandCount() > 1: rlayer.setDrawingStyle("MultiBandSingleBandGray") else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("datacatalog", "Kan WCS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.wmts = None self.wcs = 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.addWMTSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0) self.ui.addWCSbtn.setEnabled(0) self.ui.modelFilterCbx.setCurrentIndex(0)
class ConnectionDialog(QtWidgets.QDialog, DialogUi): name_le: QtWidgets.QLineEdit url_le: QtWidgets.QLineEdit authcfg_acs: qgis.gui.QgsAuthConfigSelect page_size_sb: QtWidgets.QSpinBox wfs_version_cb: QtWidgets.QComboBox detect_wfs_version_pb: QtWidgets.QPushButton network_timeout_sb: QtWidgets.QSpinBox test_connection_pb: QtWidgets.QPushButton buttonBox: QtWidgets.QDialogButtonBox options_gb: QtWidgets.QGroupBox bar: qgis.gui.QgsMessageBar detected_version_gb: qgis.gui.QgsCollapsibleGroupBox detected_version_le: QtWidgets.QLineEdit detected_capabilities_lw: QtWidgets.QListWidget api_client_class_le: QtWidgets.QLineEdit connection_id: uuid.UUID remote_geonode_version: typing.Optional[typing.Union[ packaging_version.Version, str]] discovery_task: typing.Optional[network.NetworkRequestTask] geonode_client: BaseGeonodeClient = None def __init__( self, connection_settings: typing.Optional[ConnectionSettings] = None): super().__init__() self.setupUi(self) self._widgets_to_toggle_during_connection_test = [ self.test_connection_pb, self.buttonBox, self.authcfg_acs, self.options_gb, self.connection_details, self.detected_version_gb, ] self.bar = QgsMessageBar() self.bar.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar, alignment=QtCore.Qt.AlignTop) self.discovery_task = None self._populate_wfs_version_combobox() if connection_settings is not None: self.connection_id = connection_settings.id self.remote_geonode_version = connection_settings.geonode_version self.name_le.setText(connection_settings.name) self.url_le.setText(connection_settings.base_url) self.authcfg_acs.setConfigId(connection_settings.auth_config) self.page_size_sb.setValue(connection_settings.page_size) wfs_version_index = self.wfs_version_cb.findData( connection_settings.wfs_version) self.wfs_version_cb.setCurrentIndex(wfs_version_index) if self.remote_geonode_version == network.UNSUPPORTED_REMOTE: utils.show_message( self.bar, tr("Invalid configuration. Correct GeoNode URL and/or test again." ), level=qgis.core.Qgis.Critical, ) else: self.connection_id = uuid.uuid4() self.remote_geonode_version = None self.update_connection_details() self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) ok_signals = [ self.name_le.textChanged, self.url_le.textChanged, ] for signal in ok_signals: signal.connect(self.update_ok_buttons) self.detect_wfs_version_pb.clicked.connect(self.detect_wfs_version) self.test_connection_pb.clicked.connect(self.test_connection) # disallow names that have a slash since that is not compatible with how we # are storing plugin state in QgsSettings self.name_le.setValidator( QtGui.QRegExpValidator(QtCore.QRegExp("[^\\/]+"), self.name_le)) self.update_ok_buttons() def _populate_wfs_version_combobox(self): self.wfs_version_cb.clear() for name, member in WfsVersion.__members__.items(): self.wfs_version_cb.addItem(member.value, member) v_1_1_0_index = self.wfs_version_cb.findData(WfsVersion.V_1_1_0) self.wfs_version_cb.setCurrentIndex(v_1_1_0_index) def detect_wfs_version(self): for widget in self._widgets_to_toggle_during_connection_test: widget.setEnabled(False) current_settings = self.get_connection_settings() query = QtCore.QUrlQuery() query.addQueryItem("service", "WFS") query.addQueryItem("request", "GetCapabilities") url = QtCore.QUrl(f"{current_settings.base_url}/gs/ows") url.setQuery(query) self.discovery_task = network.NetworkRequestTask( [network.RequestToPerform(url)], network_task_timeout=current_settings.network_requests_timeout, authcfg=current_settings.auth_config, description="Detect WFS version", ) self.discovery_task.task_done.connect( self.handle_wfs_version_detection_test) utils.show_message(self.bar, tr("Detecting WFS version..."), add_loading_widget=True) qgis.core.QgsApplication.taskManager().addTask(self.discovery_task) def get_connection_settings(self) -> ConnectionSettings: return ConnectionSettings( id=self.connection_id, name=self.name_le.text().strip(), base_url=self.url_le.text().strip().rstrip("/"), auth_config=self.authcfg_acs.configId(), page_size=self.page_size_sb.value(), geonode_version=self.remote_geonode_version, wfs_version=self.wfs_version_cb.currentData(), ) def test_connection(self): for widget in self._widgets_to_toggle_during_connection_test: widget.setEnabled(False) current_settings = self.get_connection_settings() self.discovery_task = network.NetworkRequestTask( [ network.RequestToPerform( QtCore.QUrl(f"{current_settings.base_url}/version.txt")) ], network_task_timeout=current_settings.network_requests_timeout, authcfg=current_settings.auth_config, description="Test GeoNode connection", ) self.discovery_task.task_done.connect(self.handle_discovery_test) utils.show_message(self.bar, tr("Testing connection..."), add_loading_widget=True) qgis.core.QgsApplication.taskManager().addTask(self.discovery_task) def handle_discovery_test(self, task_result: bool): self.enable_post_test_connection_buttons() geonode_version = network.handle_discovery_test( task_result, self.discovery_task) if geonode_version is not None: self.remote_geonode_version = geonode_version message = "Connection is valid" level = qgis.core.Qgis.Info else: message = "Connection is not valid" level = qgis.core.Qgis.Critical self.remote_geonode_version = network.UNSUPPORTED_REMOTE utils.show_message(self.bar, message, level) self.update_connection_details() def handle_wfs_version_detection_test(self, task_result: bool): self.enable_post_test_connection_buttons() # TODO: set the default to WfsVersion.AUTO when this QGIS issue has been resolved: # # https://github.com/qgis/QGIS/issues/47254 # default_version = WfsVersion.V_1_1_0 version = default_version if task_result: response_contents = self.discovery_task.response_contents[0] if response_contents is not None and response_contents.qt_error is None: raw_response = response_contents.response_body detected_versions = _get_wfs_declared_versions(raw_response) preference_order = [ "1.1.0", "2.0.0", "1.0.0", ] for preference in preference_order: if preference in detected_versions: version = WfsVersion(preference) break else: version = default_version self.bar.clearWidgets() else: utils.show_message( self.bar, tr("Unable to detect WFS version"), level=qgis.core.Qgis.Warning, ) index = self.wfs_version_cb.findData(version) self.wfs_version_cb.setCurrentIndex(index) def update_connection_details(self): invalid_version = (self.remote_geonode_version is None or self.remote_geonode_version == network.UNSUPPORTED_REMOTE) self.detected_capabilities_lw.clear() self.api_client_class_le.clear() self.detected_version_le.clear() if not invalid_version: self.detected_version_gb.setEnabled(True) current_settings = self.get_connection_settings() client: BaseGeonodeClient = apiclient.get_geonode_client( current_settings) self.detected_version_le.setText( str(current_settings.geonode_version)) self.api_client_class_le.setText(client.__class__.__name__) self.detected_capabilities_lw.insertItems( 0, [cap.name for cap in client.capabilities]) else: self.detected_version_gb.setEnabled(False) def enable_post_test_connection_buttons(self): for widget in self._widgets_to_toggle_during_connection_test: try: widget.setEnabled(True) except RuntimeError: pass self.update_ok_buttons() def accept(self): connection_settings = self.get_connection_settings() name_pattern = re.compile( f"^{connection_settings.name}$|^{connection_settings.name}(\(\d+\))$" ) duplicate_names = [] for connection_conf in settings_manager.list_connections(): if connection_conf.id == connection_settings.id: continue # we don't want to compare against ourselves if name_pattern.search(connection_conf.name) is not None: duplicate_names.append(connection_conf.name) if len(duplicate_names) > 0: connection_settings.name = ( f"{connection_settings.name}({len(duplicate_names)})") settings_manager.save_connection_settings(connection_settings) settings_manager.set_current_connection(connection_settings.id) super().accept() def update_ok_buttons(self): enabled_state = self.name_le.text() != "" and self.url_le.text() != "" self.buttonBox.button( QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled_state) self.test_connection_pb.setEnabled(enabled_state)
class BatchAlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setWindowTitle(self.tr('Batch Processing - {0}').format(self.alg.displayName())) self.setMainWidget(BatchPanel(self, self.alg)) self.textShortHelp.setVisible(False) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) def accept(self): alg_parameters = [] load = [] feedback = self.createFeedback() context = dataobjects.createContext(feedback) for row in range(self.mainWidget.tblParameters.rowCount()): col = 0 parameters = {} for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden or param.isDestination(): continue wrapper = self.mainWidget.wrappers[row][col] parameters[param.name()] = wrapper.value() if not param.checkValueIsAcceptable(wrapper.value(), context): self.bar.pushMessage("", self.tr('Wrong or missing parameter value: {0} (row {1})').format( param.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return col += 1 count_visible_outputs = 0 for out in self.alg.destinationParameterDefinitions(): if out.flags() & QgsProcessingParameterDefinition.FlagHidden: continue count_visible_outputs += 1 widget = self.mainWidget.tblParameters.cellWidget(row, col) text = widget.getValue() if param.checkValueIsAcceptable(text, context): if isinstance(out, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink)): # load rasters and sinks on completion parameters[out.name()] = QgsProcessingOutputLayerDefinition(text, context.project()) else: parameters[out.name()] = text col += 1 else: self.bar.pushMessage("", self.tr('Wrong or missing output value: {0} (row {1})').format( out.description(), row + 1), level=QgsMessageBar.WARNING, duration=5) return alg_parameters.append(parameters) QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mainWidget.setEnabled(False) self.buttonCancel.setEnabled(True) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass start_time = time.time() algorithm_results = [] for count, parameters in enumerate(alg_parameters): if feedback.isCanceled(): break self.setText(self.tr('\nProcessing algorithm {0}/{1}...').format(count + 1, len(alg_parameters))) self.setInfo(self.tr('<b>Algorithm {0} starting...</b>').format(self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) feedback.pushInfo('') alg_start_time = time.time() ret, results = execute(self.alg, parameters, context, feedback) if ret: self.setInfo(self.tr('Algorithm {0} correctly executed...').format(self.alg.displayName()), escape_html=False) feedback.setProgress(100) feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - alg_start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) feedback.pushInfo('') algorithm_results.append(results) else: break feedback.pushInfo(self.tr('Batch execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) handleAlgorithmResults(self.alg, context, feedback, False) self.finish(algorithm_results) self.buttonCancel.setEnabled(False) def finish(self, algorithm_results): for count, results in enumerate(algorithm_results): self.loadHTMLResults(results, count) self.createSummaryTable(algorithm_results) QApplication.restoreOverrideCursor() self.mainWidget.setEnabled(True) QMessageBox.information(self, self.tr('Batch processing'), self.tr('Batch processing completed')) def loadHTMLResults(self, results, num): for out in self.alg.outputDefinitions(): if isinstance(out, QgsProcessingOutputHtml) and out.name() in results and results[out.name()]: resultsList.addResult(icon=self.alg.icon(), name='{} [{}]'.format(out.description(), num), result=results[out.name()]) def createSummaryTable(self, algorithm_results): createTable = False for out in self.alg.outputDefinitions(): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)): createTable = True break if not createTable: return outputFile = getTempFilename('html') with codecs.open(outputFile, 'w', encoding='utf-8') as f: for res in algorithm_results: f.write('<hr>\n') for out in self.alg.outputDefinitions(): if isinstance(out, (QgsProcessingOutputNumber, QgsProcessingOutputString)) and out.name() in res: f.write('<p>{}: {}</p>\n'.format(out.description(), res[out.name()])) f.write('<hr>\n') resultsList.addResult(self.alg.icon(), '{} [summary]'.format(self.alg.name()), outputFile)
class LoadByClass(QtGui.QDialog, FORM_CLASS): def __init__(self, codeList, parent=None): """Constructor.""" super(LoadByClass, 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.selectedClasses = [] self.bar = QgsMessageBar() self.setLayout(QtGui.QGridLayout(self)) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setAlignment(QtCore.Qt.AlignTop) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.bar.setSizePolicy(sizePolicy) self.layout().addWidget(self.bar, 0, 0, 1, 1) #Objects Connections QtCore.QObject.connect(self.widget, QtCore.SIGNAL(("connectionChanged()")), self.listClassesFromDatabase) QtCore.QObject.connect(self.widget, QtCore.SIGNAL( ("problemOccurred()")), self.pushMessage) QtCore.QObject.connect(self.pushButtonCancel, QtCore.SIGNAL(("clicked()")), self.cancel) QtCore.QObject.connect(self.selectAllCheck, QtCore.SIGNAL(("stateChanged(int)")), self.selectAll) QtCore.QObject.connect(self.pushButtonOk, QtCore.SIGNAL(("clicked()")), self.okSelected) self.widget.tabWidget.currentChanged.connect(self.restoreInitialState) self.codeList = codeList self.layerFactory = LayerFactory() def restoreInitialState(self): self.selectedClasses = [] tam = self.classesListWidget.__len__() for i in range(tam + 1, 1, -1): item = self.classesListWidget.takeItem(i - 2) self.selectAllCheck.setCheckState(0) def listClassesFromDatabase(self): self.classes = [] self.classesListWidget.clear() self.dbVersion = self.widget.getDBVersion() self.qmlPath = self.widget.getQmlPath() self.classes = self.widget.abstractDb.listGeomClassesFromDatabase() self.classesListWidget.addItems(self.classes) self.classesListWidget.sortItems() def on_filterEdit_textChanged(self, text): classes = [ edgvClass for edgvClass in self.classes if text in edgvClass ] self.classesListWidget.clear() self.classesListWidget.addItems(classes) self.classesListWidget.sortItems() def cancel(self): self.restoreInitialState() self.close() def pushMessage(self, msg): self.bar.pushMessage("", msg, level=QgsMessageBar.WARNING) def selectAll(self): if self.selectAllCheck.isChecked(): tam = self.classesListWidget.__len__() for i in range(tam + 1): item = self.classesListWidget.item(i - 1) self.classesListWidget.setItemSelected(item, 2) else: tam = self.classesListWidget.__len__() for i in range(tam + 1): item = self.classesListWidget.item(i - 1) self.classesListWidget.setItemSelected(item, 0) def getSelectedItems(self): lista = self.classesListWidget.selectedItems() self.selectedClasses = [] tam = len(lista) for i in range(tam): self.selectedClasses.append(lista[i].text()) self.selectedClasses.sort() def okSelected(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.loadLayers() QApplication.restoreOverrideCursor() def loadLayers(self): self.getSelectedItems() if len(self.selectedClasses) > 0: try: for layer in self.selectedClasses: dbName = self.widget.abstractDb.getDatabaseName() groupList = qgis.utils.iface.legendInterface().groups() edgvLayer = self.layerFactory.makeLayer( self.widget.abstractDb, self.codeList, layer) if dbName in groupList: edgvLayer.load(self.widget.crs, groupList.index(dbName)) else: self.parentTreeNode = qgis.utils.iface.legendInterface( ).addGroup(self.widget.abstractDb.getDatabaseName(), -1) edgvLayer.load(self.widget.crs, self.parentTreeNode) self.restoreInitialState() self.close() except: self.bar.pushMessage( self.tr("Error!"), self.tr("Could not load the selected classes!"), level=QgsMessageBar.CRITICAL) else: self.bar.pushMessage(self.tr("Warning!"), self.tr("Please, select at least one class!"), level=QgsMessageBar.WARNING)
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(self.getParametersPanel(alg, self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def getParametersPanel(self, alg, parent): return ParametersPanel(parent, alg) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def getParamValues(self): parameters = {} for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): wrapper = self.mainWidget.wrappers[param.name()] value = None if wrapper.widget: value = wrapper.value() parameters[param.name()] = value if not param.checkValueIsAcceptable(value): raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ isinstance(param, (QgsProcessingParameterRasterOutput, QgsProcessingParameterFeatureSink, OutputTable)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() value = self.mainWidget.outputWidgets[param.name()].getValue() if value: value.destinationProject = dest_project parameters[param.name()] = value return parameters def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.alg.parameterDefinitions(): if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString(inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): super(AlgorithmDialog, self)._saveGeometry() context = dataobjects.createContext() checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: feedback = self.createFeedback() parameters = self.getParamValues() if checkCRS and not self.alg.validateInputCrs(parameters, context): reply = QMessageBox.question(self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) #TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question(self, self.tr("Extent CRS"), self.tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.checkParameterValues(parameters, context) if msg: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm \'{0}\' starting...</b>').format(self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) feedback.pushInfo('') start_time = time.time() if self.iterateParam: self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.alg, parameters, self.iterateParam, context, feedback): feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) self.buttonCancel.setEnabled(False) self.finish(parameters, context, feedback) else: self.buttonCancel.setEnabled(False) QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) result = executeAlgorithm(self.alg, parameters, context, feedback) feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(result)) feedback.pushInfo('') self.buttonCancel.setEnabled(False) self.finish(result, context, feedback) except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect(lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description()), level=QgsMessageBar.WARNING, duration=5) def finish(self, result, context, feedback): keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo(self.tr('Algorithm \'{0}\' finished').format(self.alg.displayName()), escape_html=False) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.hasHtmlOutputs(): self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.'), escape_html=False)
class CreatePointsOperationWizard(QWizard, WIZARD_UI): WIZARD_NAME = "CreatePointsOperationWizard" WIZARD_TOOL_NAME = QCoreApplication.translate(WIZARD_NAME, "Create Point") def __init__(self, iface, db, qgis_utils): QWizard.__init__(self) self.setupUi(self) self.iface = iface self._db = db self.qgis_utils = qgis_utils self.logger = Logger() self.names = self._db.names self.help_strings = HelpStrings() self._layers = { self.names.OP_BOUNDARY_POINT_T: { 'name': self.names.OP_BOUNDARY_POINT_T, 'geometry': None, LAYER: None }, self.names.OP_SURVEY_POINT_T: { 'name': self.names.OP_SURVEY_POINT_T, 'geometry': None, LAYER: None }, self.names.OP_CONTROL_POINT_T: { 'name': self.names.OP_CONTROL_POINT_T, 'geometry': None, LAYER: None } } self.target_layer = None # Auxiliary data to set nonlinear next pages self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3] self.dict_pages_ids = { self.pages[idx]: pid for idx, pid in enumerate(self.pageIds()) } # Set connections self.btn_browse_file.clicked.connect( make_file_selector(self.txt_file_path, file_filter=QCoreApplication.translate( "WizardTranslations", 'CSV File (*.csv *.txt)'))) self.txt_file_path.textChanged.connect(self.file_path_changed) self.crsSelector.crsChanged.connect(self.crs_changed) self.crs = QgsCoordinateReferenceSystem() self.txt_delimiter.textChanged.connect(self.fill_long_lat_combos) self.known_delimiters = [{ 'name': ';', 'value': ';' }, { 'name': ',', 'value': ',' }, { 'name': 'tab', 'value': '\t' }, { 'name': 'space', 'value': ' ' }, { 'name': '|', 'value': '|' }, { 'name': '~', 'value': '~' }, { 'name': 'Other', 'value': '' }] self.cbo_delimiter.addItems( [item['name'] for item in self.known_delimiters]) self.cbo_delimiter.currentTextChanged.connect(self.separator_changed) self.restore_settings() self.txt_file_path.textChanged.emit(self.txt_file_path.text()) self.rad_boundary_point.toggled.connect(self.point_option_changed) self.rad_control_point.toggled.connect(self.point_option_changed) self.rad_csv.toggled.connect(self.adjust_page_2_controls) self.point_option_changed() # Initialize it self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog) self.currentIdChanged.connect(self.current_page_changed) self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PointLayer) self.txt_help_page_2.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_2_OPTION_CSV) self.wizardPage2.setButtonText( QWizard.FinishButton, QCoreApplication.translate("WizardTranslations", "Import")) self.txt_help_page_3.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_3_OPTION_CSV) self.txt_help_page_3.anchorClicked.connect(self.save_template) self.button(QWizard.HelpButton).clicked.connect(self.show_help) self.rejected.connect(self.close_wizard) # Set MessageBar for QWizard self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def nextId(self): """ Set navigation order. Should return an integer. -1 is Finish. """ if self.currentId() == self.dict_pages_ids[self.wizardPage1]: return self.dict_pages_ids[self.wizardPage2] elif self.currentId() == self.dict_pages_ids[self.wizardPage2]: if self.rad_csv.isChecked(): return self.dict_pages_ids[self.wizardPage3] elif self.rad_refactor.isChecked(): return -1 elif self.currentId() == self.dict_pages_ids[self.wizardPage3]: return -1 else: return -1 def current_page_changed(self, id): """ Reset the Next button. Needed because Next might have been disabled by a condition in a another SLOT. """ enable_next_wizard(self) if id == self.dict_pages_ids[self.wizardPage2]: self.adjust_page_2_controls() elif id == self.dict_pages_ids[self.wizardPage3]: self.set_buttons_visible(False) self.set_buttons_enabled(False) QCoreApplication.processEvents() self.check_z_in_geometry() QCoreApplication.processEvents() self.fill_long_lat_combos("") QCoreApplication.processEvents() self.set_buttons_visible(True) self.set_buttons_enabled(True) def set_buttons_visible(self, visible): self.button(self.BackButton).setVisible(visible) self.button(self.FinishButton).setVisible(visible) self.button(self.CancelButton).setVisible(visible) def set_buttons_enabled(self, enabled): self.wizardPage3.setEnabled(enabled) self.button(self.BackButton).setEnabled(enabled) self.button(self.FinishButton).setEnabled(enabled) self.button(self.CancelButton).setEnabled(enabled) def check_z_in_geometry(self): self.target_layer = self.qgis_utils.get_layer( self._db, self.current_point_name(), load=True) if not self.target_layer: return if not QgsWkbTypes().hasZ(self.target_layer.wkbType()): self.labelZ.setEnabled(False) self.cbo_elevation.setEnabled(False) msg = QCoreApplication.translate( "WizardTranslations", "The current model does not support 3D geometries") self.cbo_elevation.setToolTip(msg) self.labelZ.setToolTip(msg) else: self.labelZ.setEnabled(True) self.cbo_elevation.setEnabled(True) self.labelZ.setToolTip("") self.cbo_elevation.setToolTip("") def adjust_page_2_controls(self): self.cbo_mapping.clear() self.cbo_mapping.addItem("") self.cbo_mapping.addItems( self.qgis_utils.get_field_mappings_file_names( self.current_point_name())) if self.rad_refactor.isChecked(): self.lbl_refactor_source.setEnabled(True) self.mMapLayerComboBox.setEnabled(True) self.lbl_field_mapping.setEnabled(True) self.cbo_mapping.setEnabled(True) disable_next_wizard(self) self.wizardPage2.setFinalPage(True) self.txt_help_page_2.setHtml( self.help_strings.get_refactor_help_string( self._db, self._layers[self.current_point_name()][LAYER])) elif self.rad_csv.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) enable_next_wizard(self) self.wizardPage2.setFinalPage(False) self.txt_help_page_2.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_2_OPTION_CSV) def point_option_changed(self): if self.rad_boundary_point.isChecked(): self.gbx_page_2.setTitle( QCoreApplication.translate("WizardTranslations", "Load data to Boundary Points...")) self.gbx_page_3.setTitle( QCoreApplication.translate( "WizardTranslations", "Configure CSV data source for Boundary Points...")) self.txt_help_page_1.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_1_OPTION_BP) elif self.rad_survey_point.isChecked( ): # self.rad_survey_point is checked self.gbx_page_2.setTitle( QCoreApplication.translate("WizardTranslations", "Load data to Survey Points...")) self.gbx_page_3.setTitle( QCoreApplication.translate( "WizardTranslations", "Configure CSV data source for Survey Points...")) self.txt_help_page_1.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_1_OPTION_SP) else: # self.rad_control_point is checked self.gbx_page_2.setTitle( QCoreApplication.translate("WizardTranslations", "Load data to Control Points...")) self.gbx_page_3.setTitle( QCoreApplication.translate( "WizardTranslations", "Configure CSV data source for Control Points...")) self.txt_help_page_1.setHtml( self.help_strings.WIZ_ADD_POINTS_OPERATION_PAGE_1_OPTION_CP) def finished_dialog(self): self.save_settings() if self.rad_refactor.isChecked(): output_layer_name = self.current_point_name() if self.mMapLayerComboBox.currentLayer() is not None: field_mapping = self.cbo_mapping.currentText() res_etl_model = self.qgis_utils.show_etl_model( self._db, self.mMapLayerComboBox.currentLayer(), output_layer_name, field_mapping=field_mapping) if res_etl_model: if field_mapping: self.qgis_utils.delete_old_field_mapping(field_mapping) self.qgis_utils.save_field_mapping(output_layer_name) else: self.logger.warning_msg( __name__, QCoreApplication.translate( "WizardTranslations", "Select a source layer to set the field mapping to '{}'." ).format(output_layer_name)) self.close_wizard() elif self.rad_csv.isChecked(): self.prepare_copy_csv_points_to_db() def close_wizard(self, message=None, show_message=True): if message is None: message = QCoreApplication.translate( "WizardTranslations", "'{}' tool has been closed.").format(self.WIZARD_TOOL_NAME) if show_message: self.logger.info_msg(__name__, message) self.close() def current_point_name(self): if self.rad_boundary_point.isChecked(): return self.names.OP_BOUNDARY_POINT_T elif self.rad_survey_point.isChecked(): return self.names.OP_SURVEY_POINT_T else: return self.names.OP_CONTROL_POINT_T def prepare_copy_csv_points_to_db(self): csv_path = self.txt_file_path.text().strip() if not csv_path or not os.path.exists(csv_path): self.logger.warning_msg( __name__, QCoreApplication.translate( "WizardTranslations", "No CSV file given or file doesn't exist.")) return target_layer_name = self.current_point_name() with OverrideCursor(Qt.WaitCursor): csv_layer = self.qgis_utils.csv_to_layer( csv_path, self.txt_delimiter.text(), self.cbo_longitude.currentText(), self.cbo_latitude.currentText(), self.epsg, self.cbo_elevation.currentText() or None, self.detect_decimal_point(csv_path)) self.qgis_utils.copy_csv_to_db(csv_layer, self._db, target_layer_name) def required_layers_are_available(self): layers_are_available = self.qgis_utils.required_layers_are_available( self._db, self._layers, self.WIZARD_TOOL_NAME) return layers_are_available def file_path_changed(self): self.autodetect_separator() self.fill_long_lat_combos("") self.cbo_delimiter.currentTextChanged.connect(self.separator_changed) def detect_decimal_point(self, csv_path): if os.path.exists(csv_path): with open(csv_path) as file: file.readline() # headers data = file.readline().strip() # 1st line with data if data: fields = self.get_fields_from_csv_file(csv_path) if self.cbo_latitude.currentText() in fields: num_col = data.split( self.cbo_delimiter.currentText())[fields.index( self.cbo_latitude.currentText())] for decimal_point in ['.', ',']: if decimal_point in num_col: return decimal_point return '.' # just use the default one def autodetect_separator(self): csv_path = self.txt_file_path.text().strip() if os.path.exists(csv_path): with open(csv_path) as file: first_line = file.readline() for delimiter in self.known_delimiters: if delimiter['value'] == '': continue # if separator works like a column separator in header # number of cols is greater than 1 if len(first_line.split(delimiter['value'])) > 1: self.cbo_delimiter.setCurrentText(delimiter['name']) return def update_crs_info(self): self.crsSelector.setCrs(self.crs) def crs_changed(self): authid = self.crsSelector.crs().authid() self.epsg = int(authid[5:]) def fill_long_lat_combos(self, text): csv_path = self.txt_file_path.text().strip() self.cbo_longitude.clear() self.cbo_latitude.clear() self.cbo_elevation.clear() if os.path.exists(csv_path): self.button(QWizard.FinishButton).setEnabled(True) fields = self.get_fields_from_csv_file(csv_path) fields_dict = {field: field.lower() for field in fields} if not fields: self.button(QWizard.FinishButton).setEnabled(False) return self.cbo_longitude.addItems(fields) self.cbo_latitude.addItems(fields) self.cbo_elevation.addItems([""] + fields) # Heuristics to suggest values for x, y and z x_potential_names = [ 'x', 'lon', 'long', 'longitud', 'longitude', 'este', 'east', 'oeste', 'west' ] y_potential_names = [ 'y', 'lat', 'latitud', 'latitude', 'norte', 'north' ] z_potential_names = [ 'z', 'altura', 'elevacion', 'elevation', 'elevación', 'height' ] for x_potential_name in x_potential_names: for k, v in fields_dict.items(): if x_potential_name == v: self.cbo_longitude.setCurrentText(k) break for y_potential_name in y_potential_names: for k, v in fields_dict.items(): if y_potential_name == v: self.cbo_latitude.setCurrentText(k) break if self.cbo_elevation.isEnabled(): for z_potential_name in z_potential_names: for k, v in fields_dict.items(): if z_potential_name == v: self.cbo_elevation.setCurrentText(k) break else: self.button(QWizard.FinishButton).setEnabled(False) def get_fields_from_csv_file(self, csv_path): if not self.txt_delimiter.text(): return [] error_reading = False try: reader = open(csv_path, "r") except IOError: error_reading = True line = reader.readline().replace("\n", "") reader.close() if not line: error_reading = True else: return line.split(self.txt_delimiter.text()) if error_reading: self.logger.warning_msg( __name__, QCoreApplication.translate( "WizardTranslations", "It was not possible to read field names from the CSV. Check the file and try again." )) return [] def separator_changed(self, text): # first ocurrence value = next((x['value'] for x in self.known_delimiters if x['name'] == text), '') self.txt_delimiter.setText(value) if value == '': self.txt_delimiter.setEnabled(True) else: self.txt_delimiter.setEnabled(False) def save_template(self, url): link = url.url() if self.rad_boundary_point.isChecked(): if link == '#template': self.download_csv_file('template_boundary_points.csv') elif link == '#data': self.download_csv_file('sample_boundary_points.csv') elif self.rad_survey_point.isChecked(): if link == '#template': self.download_csv_file('template_survey_points.csv') elif link == '#data': self.download_csv_file('sample_survey_points.csv') elif self.rad_control_point.isChecked(): if link == '#template': self.download_csv_file('template_control_points.csv') elif link == '#data': self.download_csv_file('sample_control_points.csv') def download_csv_file(self, filename): settings = QSettings() settings.setValue( 'Asistente-LADM_COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip()) new_filename, filter = QFileDialog.getSaveFileName( self, QCoreApplication.translate("WizardTranslations", "Save File"), os.path.join( settings.value( 'Asistente-LADM_COL/wizards/points_download_csv_path', '.'), filename), QCoreApplication.translate("WizardTranslations", "CSV File (*.csv *.txt)")) if new_filename: settings.setValue( 'Asistente-LADM_COL/wizards/points_download_csv_path', os.path.dirname(new_filename)) template_file = QFile(":/Asistente-LADM_COL/resources/csv/" + filename) if not template_file.exists(): self.logger.critical( __name__, "CSV doesn't exist! Probably due to a missing 'make' execution to generate resources..." ) msg = QCoreApplication.translate( "WizardTranslations", "CSV file not found. Update your plugin. For details see log." ) self.show_message(msg, Qgis.Warning) return if os.path.isfile(new_filename): self.logger.info( __name__, 'Removing existing file {}...'.format(new_filename)) os.chmod(new_filename, 0o777) os.remove(new_filename) if template_file.copy(new_filename): os.chmod( new_filename, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) msg = QCoreApplication.translate( "WizardTranslations", """The file <a href="file:///{}">{}</a> was successfully saved!""" ).format(normalize_local_url(new_filename), os.path.basename(new_filename)) self.show_message(msg, Qgis.Info) else: self.logger.warning( __name__, 'There was an error copying the CSV file {}!'.format( new_filename)) msg = QCoreApplication.translate( "WizardTranslations", "The file couldn\'t be saved.") self.show_message(msg, Qgis.Warning) def show_message(self, message, level): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, 10) def save_settings(self): settings = QSettings() point_type = None if self.rad_boundary_point.isChecked(): point_type = 'boundary_point' elif self.rad_survey_point.isChecked(): point_type = 'survey_point' else: point_type = 'control_point' settings.setValue('Asistente-LADM_COL/wizards/points_add_points_type', point_type) settings.setValue('Asistente-LADM_COL/wizards/points_load_data_type', 'csv' if self.rad_csv.isChecked() else 'refactor') settings.setValue( 'Asistente-LADM_COL/wizards/points_add_points_csv_file', self.txt_file_path.text().strip()) settings.setValue( 'Asistente-LADM_COL/wizards/points_csv_file_delimiter', self.txt_delimiter.text().strip()) settings.setValue('Asistente-LADM_COL/wizards/points_csv_epsg', self.epsg) def restore_settings(self): settings = QSettings() point_type = settings.value( 'Asistente-LADM_COL/wizards/points_add_points_type' ) or 'boundary_point' if point_type == 'boundary_point': self.rad_boundary_point.setChecked(True) elif point_type == 'survey_point': self.rad_survey_point.setChecked(True) else: # 'control_point' self.rad_control_point.setChecked(True) load_data_type = settings.value( 'Asistente-LADM_COL/wizards/points_load_data_type') or 'csv' if load_data_type == 'refactor': self.rad_refactor.setChecked(True) else: self.rad_csv.setChecked(True) self.txt_file_path.setText( settings.value( 'Asistente-LADM_COL/wizards/points_add_points_csv_file')) self.txt_delimiter.setText( settings.value( 'Asistente-LADM_COL/wizards/points_csv_file_delimiter')) self.crs = QgsCoordinateReferenceSystem( settings.value('Asistente-LADM_COL/wizards/points_csv_epsg', DEFAULT_EPSG, int)) self.update_crs_info() def show_help(self): self.qgis_utils.show_help("create_points")
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info") ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.png") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(self.getParametersPanel(alg, self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.runAsBatchButton = QPushButton( QCoreApplication.translate("AlgorithmDialog", "Run as Batch Process…")) self.runAsBatchButton.clicked.connect(self.runAsBatch) self.buttonBox.addButton( self.runAsBatchButton, QDialogButtonBox.ResetRole) # reset role to ensure left alignment def getParametersPanel(self, alg, parent): return ParametersPanel(parent, alg) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def getParamValues(self): parameters = {} if not hasattr(self, 'mainWidget') or self.mainWidget is None: return parameters for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): wrapper = self.mainWidget.wrappers[param.name()] value = None if wrapper.widget: value = wrapper.value() parameters[param.name()] = value if not param.checkValueIsAcceptable(value): raise AlgorithmDialogBase.InvalidParameterValue( param, wrapper.widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ isinstance(param, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination)): if self.mainWidget.checkBoxes[param.name()].isChecked(): dest_project = QgsProject.instance() value = self.mainWidget.outputWidgets[param.name()].getValue() if value and isinstance(value, QgsProcessingOutputLayerDefinition): value.destinationProject = dest_project if value: parameters[param.name()] = value return parameters def checkExtentCRS(self): unmatchingCRS = False hasExtent = False context = dataobjects.createContext() projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = QgsProcessingUtils.compatibleLayers(QgsProject.instance()) for param in self.alg.parameterDefinitions(): if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = QgsProcessingUtils.mapLayerFromString( inputlayer, context) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): if param.skip_crs_check: continue value = self.mainWidget.wrappers[ param.name()].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): super(AlgorithmDialog, self)._saveGeometry() feedback = self.createFeedback() context = dataobjects.createContext(feedback) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: parameters = self.getParamValues() if checkCRS and not self.alg.validateInputCrs(parameters, context): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) # TODO if False and checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return ok, msg = self.alg.checkParameterValues(parameters, context) if msg: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass self.setInfo( self.tr('<b>Algorithm \'{0}\' starting...</b>').format( self.alg.displayName()), escape_html=False) feedback.pushInfo(self.tr('Input parameters:')) feedback.pushCommandInfo(pformat(parameters)) feedback.pushInfo('') start_time = time.time() if self.iterateParam: self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) if executeIterating(self.alg, parameters, self.iterateParam, context, feedback): feedback.pushInfo( self.tr( 'Execution completed in {0:0.2f} seconds'.format( time.time() - start_time))) self.buttonCancel.setEnabled(False) self.finish(True, parameters, context, feedback) else: self.buttonCancel.setEnabled(False) self.resetGUI() else: command = self.alg.asPythonCommand(parameters, context) if command: ProcessingLog.addToLog(command) self.buttonCancel.setEnabled( self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel) def on_complete(ok, results): if ok: feedback.pushInfo( self.tr('Execution completed in {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo(self.tr('Results:')) feedback.pushCommandInfo(pformat(results)) else: feedback.reportError( self.tr('Execution failed after {0:0.2f} seconds'. format(time.time() - start_time))) feedback.pushInfo('') self.buttonCancel.setEnabled(False) self.finish(ok, results, context, feedback) task = QgsProcessingAlgRunnerTask(self.alg, parameters, context, feedback) task.executed.connect(on_complete) QgsApplication.taskManager().addTask(task) except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect( lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage( "", self.tr("Wrong or missing parameter value: {0}").format( e.parameter.description()), level=QgsMessageBar.WARNING, duration=5) def finish(self, successful, result, context, feedback): keepOpen = not successful or ProcessingConfig.getSetting( ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: # add html results to results dock for out in self.alg.outputDefinitions(): if isinstance(out, QgsProcessingOutputHtml) and out.name( ) in result and result[out.name()]: resultsList.addResult(icon=self.alg.icon(), name=out.description(), result=result[out.name()]) if not handleAlgorithmResults(self.alg, context, feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo(self.tr('Algorithm \'{0}\' finished').format( self.alg.displayName()), escape_html=False) if not keepOpen: self.close() else: self.resetGUI() if self.alg.hasHtmlOutputs(): self.setInfo(self.tr( 'HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.'), escape_html=False)
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(alg.getParametersPanel(self)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def setParamValues(self): params = self.alg.parameters outputs = self.alg.outputs for param in params: if param.hidden: continue wrapper = self.mainWidget.wrappers[param.name] if not self.setParamValue(param, wrapper): raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget) for output in outputs: if output.hidden: continue output.value = self.mainWidget.outputWidgets[output.name].getValue() if isinstance(output, (OutputRaster, OutputVector, OutputTable)): output.open = self.mainWidget.checkBoxes[output.name].isChecked() return True def setParamValue(self, param, wrapper): if wrapper.widget: return param.setValue(wrapper.value()) else: return True def checkExtentCRS(self): unmatchingCRS = False hasExtent = False projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = dataobjects.getAllLayers() for param in self.alg.parameters: if isinstance(param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = dataobjects.getObjectFromUri(inputlayer) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): value = self.mainWidget.wrappers[param.name].widget.leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): self.settings.setValue("/Processing/dialogBase", self.saveGeometry()) checkCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_CRS) try: self.setParamValues() if checkCRS and not self.alg.checkInputCRS(): reply = QMessageBox.question(self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting(ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) if checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question(self, self.tr("Extent CRS"), self.tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return msg = self.alg._checkParameterValuesBeforeExecuting() if msg: QMessageBox.warning( self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm %s starting...</b>') % self.alg.name) if self.iterateParam: if runalgIterating(self.alg, self.iterateParam, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.getAsCommand() if command: ProcessingLog.addToLog( ProcessingLog.LOG_ALGORITHM, command) if runalg(self.alg, self.feedback): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect(lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", "Wrong or missing parameter value: %s" % e.parameter.description, level=QgsMessageBar.WARNING, duration=5) def finish(self): keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, self.feedback, not keepOpen): self.resetGUI() return self.executed = True self.setInfo('Algorithm %s finished' % self.alg.name) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.getHTMLOutputsCount() > 0: self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.')) def closeEvent(self, evt): QgsProject.instance().layerWasAdded.disconnect(self.mainWidget.layerRegistryChanged) QgsProject.instance().layersWillBeRemoved.disconnect(self.mainWidget.layerRegistryChanged) super(AlgorithmDialog, self).closeEvent(evt)
class geopunt4QgisElevationDialog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_elevationDlg() self.ui.setupUi(self) #get settings self.s = QtCore.QSettings() self.loadSettings() self.gh = geometryHelper( self.iface ) self.eh = elevationHelper( self.iface, self.startDir) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton( QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole ) ##graph global vars self.Rubberline = None self.profile = None self.pt = None self.ax = None self.ano = None self.anoLbl = None self.counter = 0 self.xscaleUnit = (1, "m") # a figure instance to plot on self.figure = Figure() #create the Canvas widget and toolbar and set graphWgt as parent self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) ### #self.ui.toolbar.layout().insertWidget(0, self.toolbar) self.ui.graphWgt.layout().addWidget(self.canvas) self.createCanvasToolbar() #events self.ui.drawBtn.clicked.connect(self.drawBtnClicked) self.figure.canvas.mpl_connect('motion_notify_event', self.showGraphMotion) self.ui.saveLineBtn.clicked.connect(self.saveLineClicked) self.ui.savePntBtn.clicked.connect(self.savePntClicked) self.ui.addDHMbtn.clicked.connect(self.addDHMasWMS) self.ui.refreshBtn.clicked.connect( self.onRefresh ) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.rejected.connect(self.clean ) def createCanvasToolbar (self): ''' 1 Reset original view 2 Back to previous view 3 Forward to next view 4 Pan axes with left mouse, zoom with right 5 Zoom to rectangle 6 Save the figure 7 Edit curves line and axes parameters ''' self.toolbar.setVisible(False) toolbarBtns = self.ui.toolbar.findChildren(QtGui.QToolButton) self.ui.toolbar.setStyleSheet("""QToolButton {border-width: 2px; border-style: outset; border-color: #fbd837; border-radius: 5px ; background-color: white } QToolButton:pressed { border-style: inset; background-color: grey } """) toolbarBtns[0].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Keer terug naar overzicht")) toolbarBtns[0].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/full_extent.png")) toolbarBtns[0].clicked.connect( self.toolbar.home ) toolbarBtns[1].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Vorige")) toolbarBtns[1].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/previous.png")) toolbarBtns[1].clicked.connect( self.toolbar.back ) toolbarBtns[2].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Volgende")) toolbarBtns[2].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/next.png")) toolbarBtns[2].clicked.connect( self.toolbar.forward ) toolbarBtns[3].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Pannen")) toolbarBtns[3].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/pan.png")) toolbarBtns[3].clicked.connect( self.toolbar.pan ) toolbarBtns[4].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Zoom naar rechthoek")) toolbarBtns[4].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/rectangleZoom.png")) toolbarBtns[4].clicked.connect( self.toolbar.zoom ) toolbarBtns[5].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Opslaan als afbeelding")) toolbarBtns[5].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/save.png")) toolbarBtns[5].clicked.connect( self.toolbar.save_figure ) toolbarBtns[6].setToolTip(QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Vorm grafiek aanpassen")) toolbarBtns[6].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/wrench.png")) toolbarBtns[6].clicked.connect( self.toolbar.edit_parameters) toolbarBtns[7].setIcon( QtGui.QIcon(":/plugins/geopunt4Qgis/images/fill.png")) toolbarBtns[7].setToolTip( QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Kies de vulkleur")) toolbarBtns[7].clicked.connect( self.setFill) def loadSettings(self): self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if int( self.s.value("geopunt4qgis/useProxy" , 0)): self.proxy = self.s.value("geopunt4qgis/proxyHost" ,"") self.port = self.s.value("geopunt4qgis/proxyPort" ,"") else: self.proxy = "" self.port = "" self.samplesSavetoFile = int( self.s.value("geopunt4qgis/samplesSavetoFile" , 1)) sampleLayer = self.s.value("geopunt4qgis/sampleLayerTxt", "") if sampleLayer: self.sampleLayerTxt = sampleLayer self.profileLineSavetoFile = int( self.s.value("geopunt4qgis/profileLineSavetoFile" , 1)) profileLineLayer= self.s.value("geopunt4qgis/profileLineLayerTxt", "") if profileLineLayer: self.profileLineLayerTxt = profileLineLayer self.startDir = self.s.value("geopunt4qgis/startDir", os.path.dirname(__file__)) self.elevation = geopunt.elevation(self.timeout, self.proxy, self.port ) def resizeEvent(self, event): QtGui.QDialog.resizeEvent(self, event) if self.ax: try: self.figure.tight_layout() except: print str( sys.exc_info()[1] ) #eventhandlers def onRefresh(self): if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None self.plot() def onResize(self, event): self.figure.tight_layout() def openHelp(self): webbrowser.open_new_tab("http://warrieka.github.io/#!geopuntElevation.md") def drawBtnClicked(self): self.clean() #self.reSetFigure() self.tool = lineTool(self.iface, self.callBack ) self.iface.mapCanvas().setMapTool(self.tool) self.showMinimized() self.counter += 1 def showGraphMotion(self, event): if self.ax == None: return if event.xdata != None and event.ydata != None: if self.ano != None: self.ano.remove() self.ano = None if self.anoLbl != None: self.anoLbl.remove() self.anoLbl = None xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] )# if n[3] > -9999 ] zx = np.interp( event.xdata, xdata, ydata ) xmax = np.max( xdata ) xmin = np.min( xdata ) zmax = np.max( ydata ) zmin = np.max( [n[3] for n in self.profile if n[3] > -9999 ] ) if event.xdata <= xmax and event.xdata >= xmin : self.ano = self.ax.arrow( event.xdata , -9999, 0, zx + 9999, fc="k", ec="k" ) box_props = dict(boxstyle="Round,pad=0.3", fc="cyan", ec="b", lw=2) self.anoLbl = self.ax.annotate( str( round(zx, 2)) + " m", xy= (event.xdata, zx ) , xytext= (event.xdata , zx + (0.2 * ( zmax - zmin )) ), bbox=box_props ) self.setMapPt( event.xdata / self.xscaleUnit[0] ) else: self.setMapPt() event.canvas.draw() def saveLineClicked(self): if not hasattr(self, 'profileLineLayerTxt'): layerName, accept = QtGui.QInputDialog.getText(None, QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QtCore.QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.profileLineLayerTxt = layerName if self.profile != None and self.Rubberline != None: title = self.ax.get_title() self.eh.save_profile( self.Rubberline.asGeometry(), self.profile, title, self.profileLineLayerTxt, self.profileLineSavetoFile, sender=self ) def savePntClicked(self): if not hasattr(self, 'sampleLayerTxt'): layerName, accept = QtGui.QInputDialog.getText(None, QtCore.QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QtCore.QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return else: self.sampleLayerTxt = layerName if self.profile != None: title = self.ax.get_title() self.eh.save_sample_points( self.profile, title, self.sampleLayerTxt, self.samplesSavetoFile, sender=self ) def setFill( self ): if self.profile == None: return if self.ax == None: return clr = QtGui.QColorDialog.getColor( QtCore.Qt.white, self, QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Kies de vulkleur") ) if clr.isValid(): xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) self.ax.fill_between( xdata, ydata, -9999, color=clr.name() ) def addDHMasWMS(self): crs = self.iface.mapCanvas().mapRenderer().destinationCrs().authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' dhmUrl = "url=http://geo.agiv.be/inspire/wms/Hoogte&layers=DHM&format=image/png&styles=default&crs="+ crs try: rlayer = QgsRasterLayer(dhmUrl, 'Hoogtemodel', 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisElevationDialog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def plot(self): if self.Rubberline == None: return wgsLine = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() ) lineString = [ list(n) for n in wgsLine.asPolyline()] nrSamples = self.ui.nrOfSampleSpin.value() try: self.profile = self.elevation.fetchElevaton( lineString, 4326, nrSamples) except geopunt.geopuntError as ge: self.bar.pushMessage("Error", ge.message, level=QgsMessageBar.CRITICAL, duration=10) return if np.max( [n[0] for n in self.profile ] ) > 1000: self.xscaleUnit = (0.001 , "km" ) else: self.xscaleUnit = (1 , "m" ) xdata = np.array( [n[0] for n in self.profile ] ) * self.xscaleUnit[0] ydata = np.array( [n[3] for n in self.profile ] ) #need at least 3 values if len(xdata) <= 2 or len([n for n in self.profile if n[3] > -9999 ]) <= 2: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "geopunt4QgisElevationDialog", "Er werd geen of onvoldoende data gevonden"), level=QgsMessageBar.WARNING, duration=5) self.profile = None return ymin = np.min( [n[3] for n in self.profile if n[3] > -9999 ] ) ymax = np.max( ydata ) # create an axis self.ax = self.figure.add_subplot(111) # discards the old graph self.ax.hold(False) # plot data self.ax.plot( xdata, ydata,'r*') self.ax.fill_between(xdata, ydata, -9999, color='#F8E6E0' ) self.ax.set_ylim([ymin , ymax]) self.ax.set_xlim([0 , None ]) self.ax.set_ylabel("hoogte (m)") self.ax.set_xlabel("afstand (%s)" % self.xscaleUnit[1] ) self.ax.set_title("Hoogteprofiel " + str( self.counter) ) # refresh canvas self.figure.tight_layout() self.canvas.draw() def callBack(self, geom): self.iface.mapCanvas().unsetMapTool(self.tool) self.Rubberline = geom self.showNormal() self.activateWindow() self.plot() self.ui.saveWgt.setEnabled(True) def setMapPt(self, dist=None ): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if dist==None: return if self.Rubberline == None: return # dist is measured in lambert 72 in meters lb72Line = self.gh.prjLineFromMapCrs( self.Rubberline.asGeometry() , 31370 ) lb72pt = lb72Line.interpolate(dist).asPoint() pt = self.gh.prjPtToMapCrs(lb72pt, 31370) self.pt = QgsVertexMarker(self.iface.mapCanvas()) self.pt.setCenter( pt ) self.pt.setColor(QtGui.QColor(0,255,250)) self.pt.setIconSize(5) self.pt.setIconType(QgsVertexMarker.ICON_BOX ) # or ICON_CROSS, ICON_X self.pt.setPenWidth(7) if self.xscaleUnit[0] != 1: msg= "lengte= %s %s" % (round( dist * self.xscaleUnit[0], 2) , self.xscaleUnit[1]) else: msg= "lengte= %s %s" % (int( dist * self.xscaleUnit[0]) , self.xscaleUnit[1]) self.ui.mgsLbl.setText( msg ) def clean(self): if self.pt: self.iface.mapCanvas().scene().removeItem(self.pt) if self.Rubberline: self.iface.mapCanvas().scene().removeItem(self.Rubberline) if self.ano: self.ano.remove() self.ano = None if self.anoLbl: self.anoLbl.remove() self.anoLbl = None if self.ax: self.ax.hold(False) self.ax.clear() self.ax = None self.figure.clf() self.canvas.draw() self.ui.saveWgt.setEnabled(False) self.profile = None self.Rubberline = None self.ui.mgsLbl.setText("")
class ModelerParametersDialog(QDialog): def __init__(self, alg, model, algName=None, configuration=None): QDialog.__init__(self) self.setModal(True) self._alg = alg # The algorithm to define in this dialog. It is an instance of QgsProcessingAlgorithm self.model = model # The model this algorithm is going to be added to. It is an instance of QgsProcessingModelAlgorithm self.childId = algName # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.configuration = configuration self.context = createContext() self.widget_labels = {} class ContextGenerator(QgsProcessingContextGenerator): def __init__(self, context): super().__init__() self.processing_context = context def processingContext(self): return self.processing_context self.context_generator = ContextGenerator(self.context) self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.algorithmItem = None self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) widget_context = QgsProcessingParameterWidgetContext() widget_context.setProject(QgsProject.instance()) if iface is not None: widget_context.setMapCanvas(iface.mapCanvas()) widget_context.setModel(self.model) widget_context.setModelChildAlgorithmId(self.childId) self.algorithmItem = QgsGui.instance().processingGuiRegistry().algorithmConfigurationWidget(self._alg) if self.algorithmItem: self.algorithmItem.setWidgetContext(widget_context) self.algorithmItem.registerProcessingContextGenerator(self.context_generator) if self.configuration: self.algorithmItem.setConfiguration(self.configuration) self.verticalLayout.addWidget(self.algorithmItem) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper wrapper.setWidgetContext(widget_context) wrapper.registerProcessingContextGenerator(self.context_generator) if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): widget = wrapper else: widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): label = wrapper.createLabel() else: tooltip = param.description() widget.setToolTip(tooltip) label = wrapper.label self.widget_labels[param.name()] = label if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(self.tr('[Enter name if this is a final result]')) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): wrapper.setVisible(self.showAdvanced) else: wrapper.widget.setVisible(self.showAdvanced) self.widget_labels[param.name()].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, (tuple, list)): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source() == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition(value.parameterName()).description() elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition(value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): if value is None: value = QgsProcessingModelChildParameterSource.fromStaticValue(param.defaultValue()) wrapper.setWidgetValue(value) else: if value is None: value = param.defaultValue() if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() wrapper.setValue(value) for name, out in alg.modelOutputs().items(): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) if self.algorithmItem: alg.setConfiguration(self.algorithmItem.configuration()) self._alg = alg.algorithm().create(self.algorithmItem.configuration()) for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue try: wrapper = self.wrappers[param.name()] if issubclass(wrapper.__class__, WidgetWrapper): val = wrapper.value() elif issubclass(wrapper.__class__, QgsProcessingModelerParameterWidget): val = wrapper.value() else: val = wrapper.parameterValue() except InvalidParameterValue: self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format(param.description()), level=Qgis.Warning) return None if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] elif not (isinstance(val, list) and all( [isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val])): val = [QgsProcessingModelChildParameterSource.fromStaticValue(val)] for subval in val: if (isinstance(subval, QgsProcessingModelChildParameterSource) and subval.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage(self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'").format( param.description()), level=Qgis.Warning) return None alg.addParameterSources(param.name(), val) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = self.valueItems[dest.name()].text() if name.strip() != '': output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output if dest.flags() & QgsProcessingParameterDefinition.FlagIsModelOutput: if dest.name() not in outputs: output = QgsProcessingModelOutput(dest.name(), dest.name()) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[dest.name()] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append(availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) #try: # self._alg.processBeforeAddingToModeler(alg, self.model) #except: # pass return alg def okPressed(self): alg = self.createAlgorithm() if alg is not None: self.accept() def cancelPressed(self): self.reject() def openHelp(self): algHelp = self._alg.helpUrl() if not algHelp: algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format( self._alg.provider().helpId(), self._alg.groupId(), "{}{}".format(self._alg.provider().helpId(), self._alg.name()))).toString() if algHelp not in [None, ""]: webbrowser.open(algHelp)
class DlgJobs(QtGui.QDialog, Ui_DlgJobs): def __init__(self, parent=None): """Constructor.""" super(DlgJobs, self).__init__(parent) self.settings = QSettings() self.setupUi(self) self.api = API() self.bar = QgsMessageBar() self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.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) # Only enable download button if a job is selected self.download.setEnabled(False) # Set a variable used to record the necessary window width to view all # columns self._full_width = None def resizeWindowToColumns(self): if not self._full_width: margins = self.layout().contentsMargins() self._full_width = margins.left() + margins.right() + \ self.jobs_view.frameWidth() * 2 + \ self.jobs_view.verticalHeader().width() + \ self.jobs_view.horizontalHeader().length() + \ self.jobs_view.style().pixelMetric(QtGui.QStyle.PM_ScrollBarExtent) self.resize(self._full_width, self.height()) def selection_changed(self): if not self.jobs_view.selectedIndexes(): self.download.setEnabled(False) else: rows = list( set(index.row() for index in self.jobs_view.selectedIndexes())) for row in rows: # Don't set button to enabled if any of the tasks aren't yet # finished if self.jobs[row]['status'] != 'FINISHED': self.download.setEnabled(False) return self.download.setEnabled(True) def btn_refresh(self): # TODO: Handle loss of internet and connection error on button refresh # progressMessageBar = mb.createMessage("Contacting server to update job list.") # progress = QtGui.QProgressBar() # progress.setRange(0, 0) # progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) # progressMessageBar.layout().addWidget(progress) # self.bar.pushWidget(progressMessageBar, iface.messageBar().INFO) self.jobs = self.api.get_execution(user=get_user_email()) #mb.popWidget(progressMessageBar) if self.jobs: # Add script names and descriptions to jobs list self.scripts = get_scripts(self.api) 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 'original' job['raw'] = job.copy() script = job.get('script_id', None) if script: job['script_name'] = self.scripts[job['script_id']]['name'] job['script_description'] = self.scripts[ job['script_id']]['description'] else: # Handle case of scripts that have been removed or that are # no longer supported job['script_name'] = 'Script not found' job['script_description'] = 'Script not found' # Pretty print dates and pull the metadata sent as input params for job in self.jobs: job['start_date'] = datetime.strftime(job['start_date'], '%Y/%m/%d (%H:%M)') job['end_date'] = 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'] table_model = JobsTableModel(self.jobs, self) proxy_model = QtGui.QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.jobs_view.setModel(proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.jobs)): btn = QtGui.QPushButton("Details") btn.clicked.connect(self.btn_details) self.jobs_view.setIndexWidget(proxy_model.index(row, 5), btn) self.jobs_view.horizontalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) self.jobs_view.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed) self.resizeWindowToColumns() return True else: return False def btn_details(self): button = self.sender() index = self.jobs_view.indexAt(button.pos()) details_dlg = DlgJobsDetails() 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): while True: download_dir = QtGui.QFileDialog.getExistingDirectory( self, self.tr("Directory to save files"), self.settings.value("LDMP/download_dir", None), QtGui.QFileDialog.ShowDirsOnly) if download_dir: if os.access(download_dir, os.W_OK): self.settings.setValue("LDMP/download_dir", download_dir) break else: QtGui.QMessageBox.critical( None, self.tr("Error"), self.tr( "Cannot write to {}. Choose a different folder.". format(download_dir), None)) else: return False log("Downloading results to {}".format(download_dir)) rows = list( set(index.row() for index in self.jobs_view.selectedIndexes())) for row in rows: job = self.jobs[row] log("Processing job {}".format(job)) if job['results'].get('type') == 'productivity_trajectory': download_prod_traj(job, download_dir) elif job['results'].get('type') == 'productivity_state': download_prod_state(job, download_dir) elif job['results'].get('type') == 'productivity_performance': download_prod_perf(job, download_dir) elif job['results'].get('type') == 'land_cover': download_land_cover(job, download_dir) else: raise ValueError( "Unrecognized result type in download results: {}".format( dataset['dataset'])) self.close()
class AlgorithmDialog(AlgorithmDialogBase): def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.setMainWidget(ParametersPanel(self, alg)) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(0, self.bar) self.cornerWidget = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 5) self.tabWidget.setStyleSheet("QTabBar::tab { height: 30px; }") self.runAsBatchButton = QPushButton(self.tr("Run as batch process...")) self.runAsBatchButton.clicked.connect(self.runAsBatch) layout.addWidget(self.runAsBatchButton) self.cornerWidget.setLayout(layout) self.tabWidget.setCornerWidget(self.cornerWidget) def runAsBatch(self): self.close() dlg = BatchAlgorithmDialog(self.alg) dlg.show() dlg.exec_() def setParamValues(self): params = self.alg.parameters outputs = self.alg.outputs for param in params: if param.hidden: continue wrapper = self.mainWidget.wrappers[param.name] if not self.setParamValue(param, wrapper): raise AlgorithmDialogBase.InvalidParameterValue( param, wrapper.widget) for output in outputs: if output.hidden: continue output.value = self.mainWidget.valueItems[output.name].getValue() if isinstance(output, (OutputRaster, OutputVector, OutputTable)): output.open = self.mainWidget.checkBoxes[ output.name].isChecked() return True def setParamValue(self, param, wrapper, alg=None): return param.setValue(wrapper.value()) def checkExtentCRS(self): unmatchingCRS = False hasExtent = False projectCRS = iface.mapCanvas().mapSettings().destinationCrs() layers = dataobjects.getAllLayers() for param in self.alg.parameters: if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): if param.value: if isinstance(param, ParameterMultipleInput): inputlayers = param.value.split(';') else: inputlayers = [param.value] for inputlayer in inputlayers: for layer in layers: if layer.source() == inputlayer: if layer.crs() != projectCRS: unmatchingCRS = True p = dataobjects.getObjectFromUri(inputlayer) if p is not None: if p.crs() != projectCRS: unmatchingCRS = True if isinstance(param, ParameterExtent): value = self.mainWidget.valueItems[ param.name].leText.text().strip() if value: hasExtent = True return hasExtent and unmatchingCRS def accept(self): self.settings.setValue("/Processing/dialogBase", self.saveGeometry()) checkCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_CRS) try: self.setParamValues() if checkCRS and not self.alg.checkInputCRS(): reply = QMessageBox.question( self, self.tr("Unmatching CRS's"), self.tr('Layers do not all use the same CRS. This can ' 'cause unexpected results.\nDo you want to ' 'continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return checkExtentCRS = ProcessingConfig.getSetting( ProcessingConfig.WARN_UNMATCHING_EXTENT_CRS) if checkExtentCRS and self.checkExtentCRS(): reply = QMessageBox.question( self, self.tr("Extent CRS"), self. tr('Extent parameters must use the same CRS as the input layers.\n' 'Your input layers do not have the same extent as the project, ' 'so the extent might be in a wrong CRS if you have selected it from the canvas.\n' 'Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: return msg = self.alg._checkParameterValuesBeforeExecuting() if msg: QMessageBox.warning(self, self.tr('Unable to execute algorithm'), msg) return self.btnRun.setEnabled(False) self.btnClose.setEnabled(False) buttons = self.mainWidget.iterateButtons self.iterateParam = None for i in range(len(list(buttons.values()))): button = list(buttons.values())[i] if button.isChecked(): self.iterateParam = list(buttons.keys())[i] break self.progressBar.setMaximum(0) self.lblProgress.setText(self.tr('Processing algorithm...')) # Make sure the Log tab is visible before executing the algorithm try: self.tabWidget.setCurrentIndex(1) self.repaint() except: pass QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.setInfo( self.tr('<b>Algorithm %s starting...</b>') % self.alg.name) if self.iterateParam: if runalgIterating(self.alg, self.iterateParam, self): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() else: command = self.alg.getAsCommand() if command: ProcessingLog.addToLog(ProcessingLog.LOG_ALGORITHM, command) if runalg(self.alg, self): self.finish() else: QApplication.restoreOverrideCursor() self.resetGUI() except AlgorithmDialogBase.InvalidParameterValue as e: try: self.buttonBox.accepted.connect( lambda e=e: e.widget.setPalette(QPalette())) palette = e.widget.palette() palette.setColor(QPalette.Base, QColor(255, 255, 0)) e.widget.setPalette(palette) except: pass self.bar.clearWidgets() self.bar.pushMessage("", "Wrong or missing parameter value: %s" % e.parameter.description, level=QgsMessageBar.WARNING, duration=5) def finish(self): keepOpen = ProcessingConfig.getSetting( ProcessingConfig.KEEP_DIALOG_OPEN) if self.iterateParam is None: if not handleAlgorithmResults(self.alg, self, not keepOpen): self.resetGUI() return self.executed = True self.setInfo('Algorithm %s finished' % self.alg.name) QApplication.restoreOverrideCursor() if not keepOpen: self.close() else: self.resetGUI() if self.alg.getHTMLOutputsCount() > 0: self.setInfo( self.tr('HTML output has been generated by this algorithm.' '\nOpen the results dialog to check it.')) def closeEvent(self, evt): QgsMapLayerRegistry.instance().layerWasAdded.disconnect( self.mainWidget.layerRegistryChanged) QgsMapLayerRegistry.instance().layersWillBeRemoved.disconnect( self.mainWidget.layerRegistryChanged) super(AlgorithmDialog, self).closeEvent(evt)
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().insertWidget(1, self.bar) self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.splitter.restoreState(settings.value("/Processing/splitterModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.tabWidget.setCurrentIndex(0) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) factor = 1.05 if event.angleDelta().y() > 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseReleaseEvent = _mouseReleaseEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) # Set icons self.btnOpen.setIcon(QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.btnSave.setIcon(QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.btnSaveAs.setIcon(QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.btnExportImage.setIcon(QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.btnExportPython.setIcon(QgsApplication.getThemeIcon('/console/iconSaveAsConsole.png')) self.btnEditHelp.setIcon(QIcon(os.path.join(pluginPath, 'images', 'edithelp.png'))) self.btnRun.setIcon(QIcon(os.path.join(pluginPath, 'images', 'runalgorithm.png'))) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('[Enter model name here]')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('[Enter group name here]')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) self.btnOpen.clicked.connect(self.openModel) self.btnSave.clicked.connect(self.save) self.btnSaveAs.clicked.connect(self.saveAs) self.btnExportImage.clicked.connect(self.exportAsImage) self.btnExportPython.clicked.connect(self.exportAsPython) self.btnEditHelp.clicked.connect(self.editHelp) self.btnRun.clicked.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None # Indicates whether to update or not the toolbox after # closing this dialog self.update = False self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/splitterModeler", self.splitter.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Unsaved changes'), self.tr('There are unsaved changes in model. Continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ret == QMessageBox.Yes: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: QMessageBox.warning(self, self.tr('Empty model'), self.tr("Model doesn't contains any algorithms and/or " "parameters and can't be executed")) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = algList.getProviderFromName('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def exportAsImage(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in list(self.scene.items()): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) QMessageBox.information(self, self.tr('Model exported'), self.tr('Model was correctly exported.')) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: fout = codecs.open(filename, 'w', encoding='utf-8') except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return fout.write(text) fout.close() self.update = True self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.png')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) allAlgs = algList.algs for providerName in list(allAlgs.keys()): name = 'ACTIVATE_' + providerName.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[providerName].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue if text == '' or text.lower() in alg.name.lower(): if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = algList.getProviderFromName(providerName) providerItem.setText(0, provider.getDescription()) providerItem.setToolTip(0, provider.getDescription()) providerItem.setIcon(0, provider.getIcon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class BackupedImgUploaderWizard(QtGui.QWizard, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(BackupedImgUploaderWizard, 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.iface = iface self.setupUi(self) # Message bars need to be attached to pages, since the wizard object # does not have a layout. It doesn't work to attach the same bar # object to all pages (it is only shown in the last one). The only way # I could make it work was to create different QgsMessageBar objects, # one per page, but it is very to keep track of those references # along the code. It is messy, there should be a better solution. self.bar0 = QgsMessageBar() self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(0).layout().addWidget(self.bar0) self.bar1 = QgsMessageBar() self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(1).layout().addWidget(self.bar1) self.bar2 = QgsMessageBar() self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(2).layout().addWidget(self.bar2) self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload")) self.setOption(QtGui.QWizard.HaveCustomButton1, True) self.settings = settings # Dictionaries to save imagery info (todo: defined as a classes in the future) self.metadata = {} self.reprojected = [] self.licensed = [] # Initialize layers and default settings self.loadLayers() self.loadMetadataSettings() self.loadStorageSettings() self.loadOptionsSettings() # register event handlers """ List of page navigation buttons in QWizard, for reference. Please comment out and implement following functions if necessary.""" #self.button(QWizard.BackButton).clicked.connect(self.previousPage) #self.button(QWizard.NextButton).clicked.connect(self.nextPage) #self.button(QWizard.FinishButton).clicked.connect(self.finishWizard) #self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard) # Imagery connections (wizard page 1) self.layers_tool_button.clicked.connect(self.loadLayers) self.file_tool_button.clicked.connect(self.selectFile) self.add_source_button.clicked.connect(self.addSources) self.remove_source_button.clicked.connect(self.removeSources) self.up_source_button.clicked.connect(self.upSource) self.down_source_button.clicked.connect(self.downSource) # Metadata connections (wizard page 2) self.sense_start_edit.setCalendarPopup(1) self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.sense_end_edit.setCalendarPopup(1) self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.default_button.clicked.connect(self.loadMetadataSettings) self.clean_button.clicked.connect(self.cleanMetadataSettings) self.save_button.clicked.connect(self.saveMetadata) # Upload tab connections (wizard page 3) self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify) self.customButtonClicked.connect(self.startUploader) # event handling for wizard page 1 def loadLayers(self): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if not self.layers_list_widget.findItems(layer.name(), Qt.MatchExactly): item = QListWidgetItem() item.setText(layer.name()) item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri()) self.layers_list_widget.addItem(item) def selectFile(self): selected_file = QFileDialog.getOpenFileName(self, 'Select imagery file', os.path.expanduser("~")) self.source_file_edit.setText(selected_file) def addSources(self): filename = self.source_file_edit.text() selected_layers = self.layers_list_widget.selectedItems() if not filename and not selected_layers: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'Either a layer or file should be selected to be added', level=QgsMessageBar.WARNING) else: added = False if filename: if self.validateFile(filename): if not self.sources_list_widget.findItems( filename, Qt.MatchExactly): item = QListWidgetItem() item.setText(os.path.basename(filename)) item.setData(Qt.UserRole, filename) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.source_file_edit.setText('') added = True if selected_layers: for item in selected_layers: if self.validateLayer(item.text()): if not self.sources_list_widget.findItems( item.text(), Qt.MatchExactly): self.layers_list_widget.takeItem( self.layers_list_widget.row(item)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem( item.clone()) added = True if added: self.bar0.clearWidgets() self.bar0.pushMessage('INFO', 'Source(s) added to the upload queue', level=QgsMessageBar.INFO) self.loadMetadataReviewBox() def removeSources(self): selected_sources = self.sources_list_widget.selectedItems() added_sources = self.added_sources_list_widget.selectedItems() if selected_sources: for item in selected_sources: self.sources_list_widget.takeItem( self.sources_list_widget.row(item)) added_item = self.added_sources_list_widget.findItems( item.text(), Qt.MatchExactly) if added_item: self.added_sources_list_widget.takeItem( self.added_sources_list_widget.row(added_item[0])) all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if item.text() == layer.name(): if not self.layers_list_widget.findItems( item.text(), Qt.MatchExactly): self.layers_list_widget.addItem(item) else: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'An imagery source must be selected to be removed', level=QgsMessageBar.WARNING) def upSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position > 0: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position - 1, item) item.setSelected(1) def downSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position < self.sources_list_widget.count() - 1: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position + 1, item) item.setSelected(1) # event handling for wizard page 2 def loadMetadataSettings(self): self.settings.beginGroup("Metadata") self.title_edit.setText(self.settings.value('TITLE')) if self.settings.value('PLATFORM') == None: self.platform_combo_box.setCurrentIndex(0) else: self.platform_combo_box.setCurrentIndex( int(self.settings.value('PLATFORM'))) self.sensor_edit.setText(self.settings.value('SENSOR')) self.sensor_edit.setCursorPosition(0) """ self.sense_start_edit.setDateTime(QDateTime(self.settings.value('SENSE_START'))) self.sense_end_edit.setDateTime(QDateTime(self.settings.value('SENSE_END'))) """ self.sense_start_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).time()) self.tags_edit.setText(self.settings.value('TAGS')) self.tags_edit.setCursorPosition(0) self.provider_edit.setText(self.settings.value('PROVIDER')) self.provider_edit.setCursorPosition(0) self.contact_edit.setText(self.settings.value('CONTACT')) self.contact_edit.setCursorPosition(0) self.website_edit.setText(self.settings.value('WEBSITE')) self.website_edit.setCursorPosition(0) """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('LICENSE')).lower() == 'true': self.license_check_box.setCheckState(2) if str(self.settings.value('REPROJECT')).lower() == 'true': self.reproject_check_box.setCheckState(2) self.settings.endGroup() def cleanMetadataSettings(self): self.title_edit.setText('') self.platform_combo_box.setCurrentIndex(0) self.sensor_edit.setText('') self.sense_start_edit.setDate(QDateTime().fromString( '1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_start_edit.setTime(QDateTime().fromString( '1970-01-01T00:00:00', Qt.ISODate).time()) self.sense_end_edit.setDate(QDateTime().fromString( '1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_end_edit.setTime(QDateTime().fromString( '1970-01-01T00:00:00', Qt.ISODate).time()) self.tags_edit.setText('') self.provider_edit.setText('') self.contact_edit.setText('') self.website_edit.setText('') self.license_check_box.setCheckState(0) self.reproject_check_box.setCheckState(0) def saveMetadata(self): selected_layers = self.added_sources_list_widget.selectedItems() if selected_layers: for item in selected_layers: filename = item.data(Qt.UserRole) self.loadImageryInfo(filename) json_string = json.dumps(self.metadata[filename], indent=4, separators=(',', ': ')) if filename not in self.reprojected: json_filename = os.path.splitext(filename)[0] + '.json' else: # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: json_filename = os.path.splitext( filename)[0] + '_EPSG3857.json' json_file = open(json_filename, 'w') print >> json_file, json_string json_file.close() self.loadMetadataReviewBox() self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'One or more source imagery should be selected to have the metadata saved', level=QgsMessageBar.WARNING) # event handling for wizard page 3 # also see multi-thread for startUploader function def enableSpecify(self): if self.storage_combo_box.currentIndex() == 1: self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) else: self.specify_label.setEnabled(0) self.specify_edit.setText('') self.specify_edit.setEnabled(0) def loadStorageSettings(self): self.settings.beginGroup("Storage") bucket = self.settings.value('S3_BUCKET_NAME') storage_index = self.storage_combo_box.findText( bucket, Qt.MatchExactly) if not storage_index == -1: self.storage_combo_box.setCurrentIndex(storage_index) else: self.storage_combo_box.setCurrentIndex( self.storage_combo_box.findText(self.tr('other...'))) self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME')) self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID')) self.key_id_edit.setCursorPosition(0) self.secret_key_edit.setText( self.settings.value('AWS_SECRET_ACCESS_KEY')) self.secret_key_edit.setCursorPosition(0) self.settings.endGroup() def loadOptionsSettings(self): self.settings.beginGroup("Options") """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('NOTIFY_OAM')).lower() == 'true': self.notify_oam_check.setCheckState(2) if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true': self.trigger_tiling_check.setCheckState(2) self.settings.endGroup() # other functions def validateFile(self, filename): # check that file exists if not os.path.exists(filename): self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", "The file %s does not exist" % filename, level=QgsMessageBar.CRITICAL) return False # check that file is an image if imghdr.what(filename) is None: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s is not a supported data source" % filename, level=QgsMessageBar.CRITICAL) return False # check if gdal can read file try: raster = gdal.Open(filename, gdal.GA_ReadOnly) except: self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check if there is an object raster if not raster: self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check that image has at least 3 bands if raster.RasterCount < 3: self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", "The file %s has less than 3 raster bands" % filename, level=QgsMessageBar.CRITICAL) return False # check if projection is set if raster.GetProjection() is '': self.bar0.clearWidgets() self.bar0.pushMessage("CRITICAL", "Could not extract projection from file %s" % filename, level=QgsMessageBar.CRITICAL) return False # finally, check if bbox has valid data xy_points = [(0.0, 0.0), (0.0, raster.RasterYSize), (raster.RasterXSize, 0.0), (raster.RasterXSize, raster.RasterYSize)] for point in xy_points: if point != self.GDALInfoReportCorner(raster, point[0], point[1]): QgsMessageLog.logMessage('File %s is a valid data source' % filename, 'OAM', level=QgsMessageLog.INFO) return True def validateLayer(self, layer_name): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if layer_name == layer.name(): if layer.type() == QgsMapLayer.VectorLayer: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Vector layers cannot be selected for upload", level=QgsMessageBar.CRITICAL) return 0 else: return 1 def extractMetadata(self, filename): """Extract filesize, projection, bbox and gsd from image file""" self.metadata[filename]['File size'] = os.stat(filename).st_size datafile = gdal.Open(filename, gdal.GA_ReadOnly) if datafile is None: self.bar1.clearWidgets() self.bar1.pushMessage('CRITICAL', 'Extraction of raster metadata failed', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage('Failed to extract metadata', 'OAM', level=QgsMessageLog.CRITICAL) else: # extract projection projInfo = datafile.GetProjection() # WKT format spatialRef = osr.SpatialReference() spatialRef.ImportFromWkt(projInfo) # Proj4 format spatialRefProj = spatialRef.ExportToProj4() self.metadata[filename]['Projection'] = str(spatialRefProj) # original bbox upper_left = self.GDALInfoReportCorner(datafile, 0.0, 0.0) lower_left = self.GDALInfoReportCorner(datafile, 0.0, datafile.RasterYSize) upper_right = self.GDALInfoReportCorner(datafile, datafile.RasterXSize, 0.0) lower_right = self.GDALInfoReportCorner(datafile, datafile.RasterXSize, datafile.RasterYSize) center = self.GDALInfoReportCorner(datafile, datafile.RasterXSize / 2.0, datafile.RasterYSize / 2.0) # get new bbox values if reprojection will happen try: if filename in self.reprojected: self.metadata[filename]['Projection'] = "EPSG:3857" target = osr.SpatialReference() target.ImportFromEPSG(3857) transform = osr.CoordinateTransformation( spatialRef, target) point = ogr.CreateGeometryFromWkt( "POINT (%f %f)" % (upper_left[0], upper_left[1])) point.Transform(transform) upper_left = json.loads( point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt( "POINT (%f %f)" % (lower_left[0], lower_left[1])) point.Transform(transform) lower_left = json.loads( point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt( "POINT (%f %f)" % (upper_right[0], upper_right[1])) point.Transform(transform) upper_right = json.loads( point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt( "POINT (%f %f)" % (lower_right[0], lower_right[1])) point.Transform(transform) lower_right = json.loads( point.ExportToJson())['coordinates'] except (RuntimeError, TypeError, NameError) as error: print error except: print "Unexpected error:", sys.exc_info()[0] self.metadata[filename]['BBOX'] = (upper_left, lower_left, upper_right, lower_right) def GDALInfoReportCorner(self, hDataset, x, y): """GDALInfoReportCorner: extracted and adapted from the python port of gdalinfo""" # Transform the point into georeferenced coordinates adfGeoTransform = hDataset.GetGeoTransform(can_return_null=True) if adfGeoTransform is not None: dfGeoX = adfGeoTransform[ 0] + adfGeoTransform[1] * x + adfGeoTransform[2] * y dfGeoY = adfGeoTransform[ 3] + adfGeoTransform[4] * x + adfGeoTransform[5] * y else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'BBOX might be wrong. Transformation coefficient could not be fetched from raster', level=QgsMessageBar.WARNING) return (x, y) # Report the georeferenced coordinates if abs(dfGeoX) < 181 and abs(dfGeoY) < 91: return literal_eval(("(%12.7f,%12.7f) " % (dfGeoX, dfGeoY))) else: return literal_eval(("(%12.3f,%12.3f) " % (dfGeoX, dfGeoY))) def loadImageryInfoForm(self, filename): pass def loadImageryInfo(self, filename): self.metadata[filename] = {} self.metadata[filename]['Title'] = self.title_edit.text() self.metadata[filename][ 'Platform'] = self.platform_combo_box.currentIndex() self.metadata[filename]['Sensor'] = self.sensor_edit.text() start = QDateTime() start.setDate(self.sense_start_edit.date()) start.setTime(self.sense_start_edit.time()) self.metadata[filename]['Sensor start'] = start.toString(Qt.ISODate) end = QDateTime() end.setDate(self.sense_end_edit.date()) end.setTime(self.sense_end_edit.time()) self.metadata[filename]['Sensor end'] = end.toString(Qt.ISODate) self.metadata[filename]['Tags'] = self.tags_edit.text() self.metadata[filename]['Provider'] = self.provider_edit.text() self.metadata[filename]['Contact'] = self.contact_edit.text() self.metadata[filename]['Website'] = self.website_edit.text() if self.reproject_check_box.isChecked(): if filename not in self.reprojected: self.reprojected.append(filename) else: while filename in self.reprojected: self.reprojected.remove(filename) if self.license_check_box.isChecked(): self.metadata[filename][ 'License'] = "Licensed under CC-BY 4.0 and allow tracing in OSM" if filename not in self.licensed: self.licensed.append(filename) else: while filename in self.licensed: self.licensed.remove(filename) self.extractMetadata(filename) def loadMetadataReviewBox(self): json_filenames = [] for index in xrange(self.sources_list_widget.count()): filename = str( self.sources_list_widget.item(index).data(Qt.UserRole)) if filename not in self.reprojected: f = os.path.splitext(filename)[0] + '.json' else: f = os.path.splitext(filename)[0] + '_EPSG3857.json' json_filenames.append(f) temp = QTemporaryFile() temp.open() for f in json_filenames: if os.path.exists(f): with open(f) as infile: temp.write(infile.read()) temp.flush() temp.seek(0) stream = QTextStream(temp) self.review_metadata_box.setText(stream.readAll()) #functions for threading purpose def startConnection(self): if self.storage_combo_box.currentIndex() == 0: bucket_name = 'oam-qgis-plugin-test' else: bucket_name = str(self.specify_edit.text()) if not bucket_name: self.bar2.clearWidgets() self.bar2.pushMessage('WARNING', 'The bucket for upload must be provided', level=QgsMessageBar.WARNING) bucket_key = str(self.key_id_edit.text()) bucket_secret = str(self.secret_key_edit.text()) self.bucket = None for trial in xrange(3): if self.bucket: break try: connection = S3Connection(bucket_key, bucket_secret) self.bucket = connection.get_bucket(bucket_name) QgsMessageLog.logMessage('Connection established' % trial, 'OAM', level=QgsMessageLog.INFO) except: if trial == 2: QgsMessageLog.logMessage( 'Failed to connect after 3 attempts', 'OAM', level=QgsMessageLog.CRITICAL) return self.bucket def reproject(self, filename): # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: reproject_filename = os.path.splitext( filename)[0] + '_EPSG3857.tif' os.system("gdalwarp -of GTiff -t_srs epsg:3857 %s %s" % (filename, reproject_filename)) QgsMessageLog.logMessage('Reprojected to EPSG:3857', 'OAM', level=QgsMessageLog.INFO) return reproject_filename def convert(self, filename): tif_filename = os.path.splitext(filename)[0] + ".tif" #Open existing dataset src_ds = gdal.Open(filename) driver = gdal.GetDriverByName("GTiff") dst_ds = driver.CreateCopy(tif_filename, src_ds, 0) #Properly close the datasets to flush to disk dst_ds = None src_ds = None def startUploader(self): # initialize options self.upload_options = [] if self.notify_oam_check.isChecked(): self.upload_options.append("notify_oam") if self.trigger_tiling_check.isChecked(): self.upload_options.append("trigger_tiling") if self.startConnection(): for index in xrange(self.sources_list_widget.count()): filename = str( self.sources_list_widget.item(index).data(Qt.UserRole)) self.bar2.clearWidgets() self.bar2.pushMessage('INFO', 'Pre-upload image processing...', level=QgsMessageBar.INFO) # Perfom reprojection if filename in self.reprojected: filename = self.reproject(filename) QgsMessageLog.logMessage('Created reprojected file: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # Convert file format if not (imghdr.what(filename) == 'tiff'): filename = self.convert(filename) QgsMessageLog.logMessage('Converted file to tiff: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # create a new uploader instance uploader = Uploader(filename, self.bucket, self.upload_options) QgsMessageLog.logMessage('Uploader started\n', 'OAM', level=QgsMessageLog.INFO) # configure the QgsMessageBar messageBar = self.bar2.createMessage( 'INFO: Performing upload...', ) progressBar = QProgressBar() progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) messageBar.layout().addWidget(progressBar) cancelButton = QPushButton() cancelButton.setText('Cancel') cancelButton.clicked.connect(self.cancelUpload) messageBar.layout().addWidget(cancelButton) self.bar2.clearWidgets() self.bar2.pushWidget(messageBar, level=QgsMessageBar.INFO) self.messageBar = messageBar # start the worker in a new thread thread = QThread(self) uploader.moveToThread(thread) uploader.finished.connect(self.uploaderFinished) uploader.error.connect(self.uploaderError) uploader.progress.connect(progressBar.setValue) thread.started.connect(uploader.run) thread.start() self.thread = thread self.uploader = uploader else: QgsMessageLog.logMessage('No connection to the server\n', 'OAM', level=QgsMessageLog.CRITICAL) def cancelUpload(self): self.uploader.kill() self.bar2.clearWidgets() self.bar2.pushMessage('WARNING', 'Canceling upload...', level=QgsMessageBar.WARNING) def uploaderFinished(self, success): # clean up the uploader and thread try: self.uploader.deleteLater() except: QgsMessageLog.logMessage('Exception on deleting uploader\n', 'OAM', level=QgsMessageLog.CRITICAL) self.thread.quit() self.thread.wait() try: self.thread.deleteLater() except: QgsMessageLog.logMessage('Exception on deleting thread\n', 'OAM', level=QgsMessageLog.CRITICAL) # remove widget from message bar self.bar2.popWidget(self.messageBar) if success: # report the result self.bar2.clearWidgets() self.bar2.pushMessage('INFO', 'Upload completed with success', level=QgsMessageBar.INFO) QgsMessageLog.logMessage('Upload succeeded', 'OAM', level=QgsMessageLog.INFO) else: # notify the user that something went wrong self.bar2.pushMessage('CRITICAL', 'Upload was interrupted', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage('Upload was interrupted', 'OAM', level=QgsMessageLog.CRITICAL) def uploaderError(self, e, exception_string): QgsMessageLog.logMessage( 'Uploader thread raised an exception:\n'.format(exception_string), 'OAM', level=QgsMessageLog.CRITICAL)
class CreateGroupPartyCadastre(QDialog, DIALOG_UI): def __init__(self, iface, db, qgis_utils, parent=None): QDialog.__init__(self) self.setupUi(self) self.iface = iface self.log = QgsApplication.messageLog() self._la_group_party_table = None self._members_table = None self._fraction_table = None self._col_party_layer = None self._db = db self.qgis_utils = qgis_utils self.help_strings = HelpStrings() self.data = {} # {t_id: [display_text, denominator, numerator]} self.current_selected_parties = [] # [t_ids] self.parties_to_group = {} # {t_id: [denominator, numerator]} # Fill combo of types la_group_party_type_table = self.qgis_utils.get_layer( self._db, LA_GROUP_PARTY_TYPE_TABLE, None, True) if la_group_party_type_table is None: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate( "CreateGroupPartyCadastreWizard", "Group Party Type Table couldn't be found... {}").format( self._db.get_description()), Qgis.Warning) return domain_key_index = la_group_party_type_table.fields().indexOf( DOMAIN_KEY_FIELD[self._db.mode]) domain_keys = list( la_group_party_type_table.uniqueValues(domain_key_index)) domain_keys.sort() self.cbo_group_type.addItems(domain_keys) self.txt_search_party.setText("") self.btn_select.setEnabled(False) self.btn_deselect.setEnabled(False) self.tbl_selected_parties.setColumnCount(3) self.tbl_selected_parties.setColumnWidth(0, 140) self.tbl_selected_parties.setColumnWidth(1, 90) self.tbl_selected_parties.setColumnWidth(2, 90) self.tbl_selected_parties.sortItems(0, Qt.AscendingOrder) self.txt_search_party.textEdited.connect(self.search) self.lst_all_parties.itemSelectionChanged.connect( self.selection_changed_all) self.tbl_selected_parties.itemSelectionChanged.connect( self.selection_changed_selected) self.tbl_selected_parties.cellChanged.connect(self.valueEdited) self.btn_select_all.clicked.connect(self.select_all) self.btn_deselect_all.clicked.connect(self.deselect_all) self.btn_select.clicked.connect(self.select) self.btn_deselect.clicked.connect(self.deselect) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def validate_target_layers(self): # Get the required target layers and validate if edit session is closed res_layers = self.qgis_utils.get_layers(self._db, { LA_GROUP_PARTY_TABLE: { 'name': LA_GROUP_PARTY_TABLE, 'geometry': None }, MEMBERS_TABLE: { 'name': MEMBERS_TABLE, 'geometry': None }, FRACTION_TABLE: { 'name': FRACTION_TABLE, 'geometry': None } }, load=True) self._la_group_party_table = res_layers[LA_GROUP_PARTY_TABLE] if self._la_group_party_table is None: return (False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Group party table couldn't be found... {}").format( self._db.get_description())) if self._la_group_party_table.isEditable(): return ( False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Close the edit session in table {} before creating group parties." ).format(self._la_group_party_table.name())) self._members_table = res_layers[MEMBERS_TABLE] if self._members_table is None: return (False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Members table couldn't be found... {}").format( self._db.get_description())) if self._members_table.isEditable(): return ( False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Close the edit session in table {} before creating group parties." ).format(self._members_table.name())) self._fraction_table = res_layers[FRACTION_TABLE] if self._fraction_table is None: return (False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Fraction table couldn't be found... {}").format( self._db.get_description())) if self._fraction_table.isEditable(): return ( False, QCoreApplication.translate( "CreateGroupPartyCadastre", "Close the edit session in table {} before creating group parties." ).format(self._fraction_table.name())) return (True, None) def set_parties_data(self, parties_data): """ Initialize parties data. :param parties_data: Dictionary {t_id: [display_text, denominator, numerator]} :type parties_data: dict """ self.data = parties_data self.update_lists() def search(self, text): self.update_lists(True) def selection_changed_all(self): self.btn_select.setEnabled(len(self.lst_all_parties.selectedItems())) def selection_changed_selected(self): self.btn_deselect.setEnabled( len(self.tbl_selected_parties.selectedItems())) def select_all(self): """ SLOT. Select all parties listed from left list widget. """ items_ids = [] for index in range(self.lst_all_parties.count()): items_ids.append( self.lst_all_parties.item(index).data(Qt.UserRole)) self.add_parties_to_selected(items_ids) def deselect_all(self): """ SLOT. Remove all parties from left list widget. """ items_ids = [] for index in range(self.tbl_selected_parties.rowCount()): items_ids.append( self.tbl_selected_parties.item(index, 0).data(Qt.UserRole)) self.remove_parties_from_selected(items_ids) def select(self): """ SLOT. Select all parties highlighted in left list widget. """ self.add_parties_to_selected([ item.data(Qt.UserRole) for item in self.lst_all_parties.selectedItems() ]) def deselect(self): """ SLOT. Remove all parties highlighted in right list widget. """ self.remove_parties_from_selected([ item.data(Qt.UserRole) for item in self.tbl_selected_parties.selectedItems() if item.column() == 0 ]) def add_parties_to_selected(self, parties_ids): self.current_selected_parties.extend(parties_ids) self.update_lists() def remove_parties_from_selected(self, parties_ids): for party_id in parties_ids: self.current_selected_parties.remove(party_id) if party_id in self.parties_to_group: del self.parties_to_group[party_id] self.update_lists() def update_lists(self, only_update_all_list=False): """ Update left list widget and optionally the right one. :param only_update_all_list: Only updat left list widget. :type only_update_all_list: bool """ # All parties self.lst_all_parties.clear() if self.txt_search_party.text(): tmp_parties = { i: d for i, d in self.data.items() if self.txt_search_party.text().lower() in d[0].lower() } else: tmp_parties = copy.deepcopy(self.data) # Copy all! for party_id in self.current_selected_parties: if party_id in tmp_parties: del tmp_parties[party_id] for i, d in tmp_parties.items(): item = QListWidgetItem(d[0]) item.setData(Qt.UserRole, i) self.lst_all_parties.addItem(item) if not only_update_all_list: # Selected parties self.tbl_selected_parties.clearContents() self.tbl_selected_parties.setRowCount( len(self.current_selected_parties)) self.tbl_selected_parties.setColumnCount(3) self.tbl_selected_parties.setSortingEnabled(False) for row, party_id in enumerate(self.current_selected_parties): item = QTableWidgetItem(self.data[party_id][0]) item.setFlags(item.flags() & ~Qt.ItemIsEditable) item.setData(Qt.UserRole, party_id) self.tbl_selected_parties.setItem(row, 0, item) value_denominator = self.parties_to_group[party_id][ 0] if party_id in self.parties_to_group else self.data[ party_id][1] self.tbl_selected_parties.setItem( row, 1, QTableWidgetItem(str(value_denominator))) value_numerator = self.parties_to_group[party_id][ 1] if party_id in self.parties_to_group else self.data[ party_id][2] self.tbl_selected_parties.setItem( row, 2, QTableWidgetItem(str(value_numerator))) self.tbl_selected_parties.setSortingEnabled(True) def valueEdited(self, row, column): """ SLOT. Update either the denominator or the numerator for given row. :param row: Edited row :type row: int :param column: Edited column :type column: int """ if column != 0: party_id = self.tbl_selected_parties.item(row, 0).data(Qt.UserRole) value_denominator = self.tbl_selected_parties.item(row, 1).text() # While creating a row and the second column is created, the third # one doesn't exist, so use the value already stored for that case value_numerator = self.parties_to_group[party_id][ 1] if party_id in self.parties_to_group else 0 if self.tbl_selected_parties.item(row, 2) is not None: value_numerator = self.tbl_selected_parties.item(row, 2).text() self.parties_to_group[party_id] = [ value_denominator, value_numerator ] def accept(self): """ Overwrite the dialog's `accept <https://doc.qt.io/qt-5/qdialog.html#accept>`_ SLOT to store selected parties and numerator-denominator before closing the dialog. """ self.parties_to_group = {} for index in range(self.tbl_selected_parties.rowCount()): k = self.tbl_selected_parties.item(index, 0).data(Qt.UserRole) try: v_n = int(self.tbl_selected_parties.item(index, 1).text()) except ValueError as e: self.show_message( QCoreApplication.translate( "CreateGroupPartyCadastreWizard", "There are some invalid values in the numerator column. Fix them before continuing..." ), Qgis.Warning) return try: v_d = int(self.tbl_selected_parties.item(index, 2).text()) except ValueError as e: self.show_message( QCoreApplication.translate( "CreateGroupPartyCadastreWizard", "There are some invalid values in the denominator column. Fix them before continuing..." ), Qgis.Warning) return self.parties_to_group[k] = [v_n, v_d] name = self.txt_group_name.text() group_party_type = self.cbo_group_type.currentText() dict_params = { LA_GROUP_PARTY_NAME_FIELD: name, LA_GROUP_PARTY_GPTYPE_FIELD: group_party_type, 'porcentajes': self.parties_to_group } res, msg = self.validate_group_party(dict_params) if not res: self.show_message(msg, Qgis.Warning) return self.save_group_party(self._db, [dict_params]) self.close() def validate_group_party(self, params): name = params[LA_GROUP_PARTY_NAME_FIELD] group_party_type = params[LA_GROUP_PARTY_GPTYPE_FIELD] porcentajes = params['porcentajes'] if not porcentajes: return (False, QCoreApplication.translate( "CreateGroupParty", "You need to select some parties to create a group.")) elif len(porcentajes) == 1: return ( False, QCoreApplication.translate( "CreateGroupParty", "There is just one party, you need to add at least two parties to a group." )) there_percents = False fraction = 0 for t, nd in porcentajes.items(): if porcentajes[t] != [0, 0]: there_percents = True break if there_percents: for t, nd in porcentajes.items(): if porcentajes[t][1] == 0: return ( False, QCoreApplication.translate( "CreateGroupParty", "There are denominators equal to zero. You need to change those values." )) break elif porcentajes[t][1] < porcentajes[t][0]: return ( False, QCoreApplication.translate( "CreateGroupParty", "The denominator cannot be less than the numerator." )) break else: fraction = porcentajes[t][0] / porcentajes[t][1] + fraction if fraction != 1.0: return (False, QCoreApplication.translate( "CreateGroupParty", "The sum of the fractions must be equal to one.")) return (True, QCoreApplication.translate("CreateGroupParty", "Validation passed!")) def show_message(self, message, level): self.bar.pushMessage(message, level, 10) def save_group_party(self, db, params): """ Save group party data into associated tables: LA_GROUP_PARTY_TABLE, MEMBERS_TABLE and FRACTION_TABLE. params: List of dicts, where each dict is an independent group party: { LA_GROUP_PARTY_NAME_FIELD: '', LA_GROUP_PARTY_GPTYPE_FIELD: '', 'porcentajes': { 't_id_miembro': [20, 100], # numerador/denominador 't_id_miembro2': [40, 100] } } """ # Disconnect from previous runs try: self._la_group_party_table.committedFeaturesAdded.disconnect() except TypeError as e: pass try: self._members_table.committedFeaturesAdded.disconnect() except TypeError as e: pass for group in params: # Create connections to react when a group party is stored to the DB self._la_group_party_table.committedFeaturesAdded.connect( partial(self.finish_group_party_saving, group['porcentajes'])) # First save the group party new_feature = QgsVectorLayerUtils().createFeature( self._la_group_party_table) new_feature.setAttribute(LA_GROUP_PARTY_GPTYPE_FIELD, group[LA_GROUP_PARTY_GPTYPE_FIELD]) new_feature.setAttribute(LA_GROUP_PARTY_NAME_FIELD, group[LA_GROUP_PARTY_NAME_FIELD]) new_feature.setAttribute(LA_GROUP_PARTY_TYPE_FIELD, LA_GROUP_PARTY_TYPE_VALUE) # TODO: Gui should allow users to ented namespace, local_id and date values #new_feature.setAttribute("p_espacio_de_nombres", LA_GROUP_PARTY_TABLE) #new_feature.setAttribute("p_local_id", '0') #new_feature.setAttribute("comienzo_vida_util_version", 'now()') self.log.logMessage("Saving Group Party: {}".format(group), PLUGIN_NAME, Qgis.Info) with edit(self._la_group_party_table): self._la_group_party_table.addFeature(new_feature) def finish_group_party_saving(self, members, layer_id, features): try: self._la_group_party_table.committedFeaturesAdded.disconnect() except TypeError as e: pass if len(features) != 1: self.log.logMessage( "We should have got only one group party... We cannot do anything with {} group parties" .format(len(features)), PLUGIN_NAME, Qgis.Warning) else: fid = features[0].id() if not self._la_group_party_table.getFeature(fid).isValid(): self.log.logMessage( "Feature not found in table Group Party...", PLUGIN_NAME, Qgis.Warning) else: group_party_id = self._la_group_party_table.getFeature( fid)[ID_FIELD] # Now save members for party_id, fraction in members.items(): # Create connections to react when a group party is stored to the DB self._members_table.committedFeaturesAdded.connect( partial(self.finish_member_saving, fraction)) new_feature = QgsVectorLayerUtils().createFeature( self._members_table) new_feature.setAttribute(MEMBERS_GROUP_PARTY_FIELD, group_party_id) new_feature.setAttribute(MEMBERS_PARTY_FIELD, party_id) self.log.logMessage( "Saving group party's member ({}: {}).".format( group_party_id, party_id), PLUGIN_NAME, Qgis.Info) with edit(self._members_table): self._members_table.addFeature(new_feature) def finish_member_saving(self, fraction, layer_id, features): try: self._members_table.committedFeaturesAdded.disconnect() except TypeError as e: pass if len(features) != 1: self.log.logMessage( "We should have got only one member... We cannot do anything with {} members" .format(len(features)), PLUGIN_NAME, Qgis.Warning) else: fid = features[0].id() if not self._members_table.getFeature(fid).isValid(): self.log.logMessage("Feature not found in table Members...", PLUGIN_NAME, Qgis.Warning) else: member_id = self._members_table.getFeature(fid)[ID_FIELD] if fraction == [0, 0]: return # And finally save fractions new_feature = QgsVectorLayerUtils().createFeature( self._fraction_table) new_feature.setAttribute(FRACTION_MEMBER_FIELD, member_id) new_feature.setAttribute(FRACTION_NUMERATOR_FIELD, fraction[0]) new_feature.setAttribute(FRACTION_DENOMINATOR_FIELD, fraction[1]) with edit(self._fraction_table): self.log.logMessage( "Saving member's fraction ({}: {}).".format( member_id, fraction), PLUGIN_NAME, Qgis.Info) self._fraction_table.addFeature(new_feature) def show_help(self): self.qgis_utils.show_help("group_party")
class ImgSearchDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(ImgSearchDialog, 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.iface = iface self.setupUi(self) self.settings = settings self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint) # initialize GUI self.initGui() # self.buttonBox.button(QDialogButtonBox.Ok).setText('Save') self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayoutListWidget.layout().addWidget(self.bar) # event handling self.pushButtonSearch.clicked.connect(self.startSearch) # self.returnPressed.connect(self.startSearch) # self.pushButtonSearchLatest.clicked.connect(self.searchLatest) # self.pushButtonBrowseLocation.clicked.connect(self.browseLocation) self.connect(self.listWidget, QtCore.SIGNAL( "itemClicked(QListWidgetItem *)"), self.browseThumbnailAndMeta) # self.buttonBox.clicked.connect(lambda: self.test(self.buttonBox)) self.connect(self.buttonBox, QtCore.SIGNAL('accepted()'), self.execOk) self.connect(self.buttonBox, QtCore.SIGNAL('rejected()'), self.execCancel) # disable some GUIs # self.pushButtonBrowseLocation.hide() # self.pushButtonSearchLatest.hide() # add objects for catalog access self.settings.beginGroup("Storage") if self.settings.value('CATALOG_URL') is None or \ str(self.settings.value('CATALOG_URL')) == '': # self.catalogUrl = "https://oam-catalog.herokuapp.com" self.catalogUrl = "http://api.openaerialmap.org" else: self.catalogUrl = str(self.settings.value('CATALOG_URL')) self.settings.endGroup() self.oamCatalogAccess = OAMCatalogAccess(self.catalogUrl) catalogUrlLabel = self.catalog_url_label.text() + self.catalogUrl self.catalog_url_label.setText(catalogUrlLabel) self.imgBrowser = None def closeEvent(self, evnt): pass def keyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Return: self.startSearch() def test(self, *argv): print(str(argv)) def createQueriesSettings(self): self.settings.setValue('CHECKBOX_LOCATION', True) self.settings.setValue('CHECKBOX_ACQUISITION_FROM', True) self.settings.setValue('CHECKBOX_ACQUISITION_TO', True) self.settings.setValue('CHECKBOX_GSD_FROM', True) self.settings.setValue('CHECKBOX_GSD_TO', True) self.settings.setValue('LOCATION', '') self.settings.setValue('ACQUISITION_FROM', QDate.currentDate().addMonths(-12).toString(Qt.ISODate)) self.settings.setValue('ACQUISITION_TO', QDate.currentDate().toString(Qt.ISODate)) self.settings.setValue('GSD_FROM', '') self.settings.setValue('GSD_TO', '') self.settings.setValue('LIMIT', 20) self.settings.setValue('ORDER_BY', 0) self.settings.setValue('SORT', 'desc') def loadQueriesSettings(self): self.settings.beginGroup("ImageSearch") if str(self.settings.value('CHECKBOX_LOCATION')).lower() == 'true': self.checkBoxLocation.setCheckState(2) else: self.checkBoxLocation.setCheckState(0) if str(self.settings.value('CHECKBOX_ACQUISITION_FROM')).lower() == 'true': self.checkBoxAcquisitionFrom.setCheckState(2) else: self.checkBoxAcquisitionFrom.setCheckState(0) if str(self.settings.value('CHECKBOX_ACQUISITION_TO')).lower() == 'true': self.checkBoxAcquisitionTo.setCheckState(2) else: self.checkBoxAcquisitionTo.setCheckState(0) if str(self.settings.value('CHECKBOX_GSD_FROM')).lower() == 'true': self.checkBoxResolutionFrom.setCheckState(2) else: self.checkBoxResolutionFrom.setCheckState(0) if str(self.settings.value('CHECKBOX_GSD_TO')).lower() == 'true': self.checkBoxResolutionTo.setCheckState(2) else: self.checkBoxResolutionTo.setCheckState(0) self.lineEditLocation.setText( self.settings.value('LOCATION')) self.dateEditAcquisitionFrom.setDate(QDate.fromString( self.settings.value('ACQUISITION_FROM'), Qt.ISODate)) self.dateEditAcquisitionTo.setDate(QDate.fromString( self.settings.value('ACQUISITION_TO'), Qt.ISODate)) self.lineEditResolutionFrom.setText( str(self.settings.value('GSD_FROM'))) self.lineEditResolutionTo.setText( str(self.settings.value('GSD_TO'))) self.lineEditNumImages.setText( str(self.settings.value('LIMIT'))) self.comboBoxOrderBy.setCurrentIndex( int(self.settings.value('ORDER_BY'))) if self.settings.value('SORT') == 'desc': self.radioButtonDesc.setChecked(True) else: self.radioButtonAsc.setChecked(True) self.settings.endGroup() def saveQueriesSettings(self): self.settings.beginGroup("ImageSearch") self.settings.setValue('CHECKBOX_LOCATION', self.checkBoxLocation.isChecked()) self.settings.setValue('CHECKBOX_ACQUISITION_FROM', self.checkBoxAcquisitionFrom.isChecked()) self.settings.setValue('CHECKBOX_ACQUISITION_TO', self.checkBoxAcquisitionTo.isChecked()) self.settings.setValue('CHECKBOX_GSD_FROM', self.checkBoxResolutionFrom.isChecked()) self.settings.setValue('CHECKBOX_GSD_TO', self.checkBoxResolutionTo.isChecked()) self.settings.setValue('LOCATION', self.lineEditLocation.text()) self.settings.setValue('ACQUISITION_FROM', self.dateEditAcquisitionFrom.date().toString(Qt.ISODate)) self.settings.setValue('ACQUISITION_TO', self.dateEditAcquisitionTo.date().toString(Qt.ISODate)) if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): self.settings.setValue('GSD_FROM', float(self.lineEditResolutionFrom.text())) if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): self.settings.setValue('GSD_TO', float(self.lineEditResolutionTo.text())) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): self.settings.setValue('LIMIT', int(self.lineEditNumImages.text())) self.settings.setValue('ORDER_BY', self.comboBoxOrderBy.currentIndex()) if self.radioButtonDesc.isChecked(): self.settings.setValue('SORT', 'desc') else: self.settings.setValue('SORT', 'asc') self.settings.endGroup() def initGui(self): item = QListWidgetItem() item.setText("Please set the conditions and press 'Search' button.") item.setData(Qt.UserRole, "Sample Data") self.listWidget.addItem(item) # load default queries self.settings.beginGroup("ImageSearch") if self.settings.value('CHECKBOX_LOCATION') is None: print('create new queries settings') self.createQueriesSettings() self.settings.endGroup() self.loadQueriesSettings() def refreshListWidget(self, metadataInList): self.listWidget.clear() for singleMetaInDict in metadataInList: item = QListWidgetItem() item.setText(singleMetaInDict[u'title']) item.setData(Qt.UserRole, singleMetaInDict) self.listWidget.addItem(item) def startSearch(self): action = "meta" dictQueries = {} try: if self.checkBoxLocation.isChecked(): location = self.lineEditLocation.text() strBboxForOAM = nominatim_search(location) print(strBboxForOAM) if strBboxForOAM != 'failed': dictQueries['bbox'] = strBboxForOAM else: qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("The 'location' won't be used as a " + "query, because Geocoder could " + "not find the location.") qMsgBox.exec_() if self.checkBoxAcquisitionFrom.isChecked(): dictQueries['acquisition_from'] = \ self.dateEditAcquisitionFrom.date().toString(Qt.ISODate) if self.checkBoxAcquisitionTo.isChecked(): dictQueries['acquisition_to'] = \ self.dateEditAcquisitionTo.date().toString(Qt.ISODate) if self.checkBoxResolutionFrom.isChecked(): if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): dictQueries['gsd_from'] = \ float(self.lineEditResolutionFrom.text()) if self.checkBoxResolutionTo.isChecked(): if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): dictQueries['gsd_to'] = \ float(self.lineEditResolutionTo.text()) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): dictQueries['limit'] = int(self.lineEditNumImages.text()) if self.comboBoxOrderBy.currentText() == 'Acquisition Date': dictQueries['order_by'] = "acquisition_end" elif self.comboBoxOrderBy.currentText() == 'GSD': dictQueries['order_by'] = "gsd" print(self.comboBoxOrderBy.currentText()) if self.radioButtonAsc.isChecked(): dictQueries['sort'] = "asc" elif self.radioButtonDesc.isChecked(): dictQueries['sort'] = "desc" self.oamCatalogAccess.setAction(action) self.oamCatalogAccess.setDictQueries(dictQueries) metadataInList = self.oamCatalogAccess.getMetadataInList() self.refreshListWidget(metadataInList) except Exception as e: print(repr(e)) qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("Please make sure if you entered valid data" + "/internet connection, and try again.") qMsgBox.exec_() """ This function is not in use. """ """ def searchLatest(self): action = "meta" dictQueries = {} try: dictQueries['sort'] = "desc" dictQueries['order_by'] = "acquisition_end" if (self.lineEditResolutionFrom.text() != '' and self.lineEditResolutionFrom.text() is not None): dictQueries['gsd_from'] = float( self.lineEditResolutionFrom.text()) if (self.lineEditResolutionTo.text() != '' and self.lineEditResolutionTo.text() is not None): dictQueries['gsd_to'] = float( self.lineEditResolutionTo.text()) if (self.lineEditNumImages.text() != '' and self.lineEditNumImages.text() is not None): dictQueries['limit'] = int(self.lineEditNumImages.text()) self.oamCatalogAccess.setAction(action) self.oamCatalogAccess.setDictQueries(dictQueries) metadataInList = self.oamCatalogAccess.getMetadataInList() self.refreshListWidget(metadataInList) except Exception as e: qMsgBox = QMessageBox() qMsgBox.setWindowTitle('Message') qMsgBox.setText("Please make sure if you entered " + "valid data/internet connection, and try again.") qMsgBox.exec_() """ """ This function is not in use. """ """ def browseLocation(self): print("Browse location of loaded layer...") """ def browseThumbnailAndMeta(self, item): singleMetaInDict = item.data(Qt.UserRole) # print(str(singleMetaInDict)) if type(singleMetaInDict) is dict: if self.imgBrowser is None: self.imgBrowser = ImgBrowser(self.iface) self.imgBrowser.thumbnailManager.statusChanged.connect( self.changeThumbnailStatus) self.imgBrowser.thumbnailManager.error.connect( self.displayThumnailDownloadError) pos = self.pos() print(pos.x()) print(pos.y()) pos.setX(pos.x() + 400) pos.setY(pos.y() + 20) self.imgBrowser.move(pos) self.imgBrowser.setSingleMetaInDic(singleMetaInDict) self.imgBrowser.displayMetadata() self.imgBrowser.displayThumbnail() if not self.imgBrowser.isVisible(): self.imgBrowser.show() self.imgBrowser.activateWindow() def changeThumbnailStatus(self, status): # print(str(status)) if status == 0: pass """ self.bar.clearWidgets() self.bar.pushMessage( 'INFO', 'Downloading thumbnail...', level=QgsMessageBar.INFO, duration=8) """ if status == 1: self.bar.clearWidgets() def displayThumnailDownloadError(self, e): self.bar.clearWidgets() self.bar.pushMessage( 'Failed to download thumbnail.\n{}'.format(str(e)), level=QgsMessageBar.WARNING, duration=8) def execOk(self): # save self.defaultQueriesInDict into self.settings self.saveQueriesSettings() self.close() def execCancel(self): self.close()
class ShellOutputScintilla(QgsPythonConsoleBase): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) self.setCaretWidth(0) # NO (blinking) caret in the output self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.setContext(Qt.WidgetWithChildrenShortcut) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can harm your computer" ) # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() self.setSelectionForegroundColor( self.color(QgsCodeEditor.ColorRole.SelectionForeground)) self.setSelectionBackgroundColor( self.color(QgsCodeEditor.ColorRole.SelectionBackground)) self.setMarginsForegroundColor( self.color(QgsCodeEditor.ColorRole.MarginForeground)) self.setMarginsBackgroundColor( self.color(QgsCodeEditor.ColorRole.MarginBackground)) self.setCaretLineBackgroundColor( self.color(QgsCodeEditor.ColorRole.CaretLine)) self.setCaretForegroundColor(self.color( QgsCodeEditor.ColorRole.Cursor)) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) menu.addAction( self.iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( self.iconShowEditor, QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( self.iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( self.iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) pyQGISHelpAction = menu.addAction( self.iconPyQGISHelp, QCoreApplication.translate("PythonConsole", "Search Selected in PyQGIS docs"), self.searchPyQGIS) menu.addSeparator() copyAction = menu.addAction( self.iconCopy, QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(self.iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) pyQGISHelpAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) pyQGISHelpAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class geopunt4QgisDataCatalog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale")[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', 'geopunt4qgis_{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.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 = QtCore.QSettings() self.loadSettings() self.gh = gh.geometryHelper( self.iface ) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed ) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton( QtGui.QPushButton("Sluiten"), QtGui.QDialogButtonBox.RejectRole ) #vars self.firstShow = True self.wms = None self.wfs = None self.dl = None self.zoek = '' self.bronnen = None self.model = QtGui.QStandardItemModel( self ) self.proxyModel = QtGui.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel( self.proxyModel ) self.completer = QtGui.QCompleter( self ) self.completerModel = QtGui.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.finished.connect(self.clean) def loadSettings(self): self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if int( self.s.value("geopunt4qgis/useProxy" , 0)): self.proxy = self.s.value("geopunt4qgis/proxyHost" ,"") self.port = self.s.value("geopunt4qgis/proxyPort" ,"") else: self.proxy = "" self.port = "" self.md = metadata.MDReader( self.timeout, self.proxy, self.port ) def _setModel(self, records): self.model.clear() for rec in records: title = QtGui.QStandardItem( rec['title'] ) #0 wms = QtGui.QStandardItem( rec['wms'] ) #1 downloadLink = QtGui.QStandardItem(rec['download']) #2 id = QtGui.QStandardItem( rec['uuid'] ) #3 abstract = QtGui.QStandardItem( rec['abstract'] ) #4 wfs = QtGui.QStandardItem( rec['wfs'] ) #5 self.model.appendRow([title,wms,downloadLink,id,abstract,wfs]) #overwrite def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on( proxyUrl=self.proxy, port=self.port, timeout=self.timeout ) if inet: 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 else: self.bar.pushMessage( QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Kan geen verbing maken met het internet."), level=QgsMessageBar.WARNING, duration=3) #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/><br/> <a href='https://metadata.geopunt.be/zoekdienst/apps/tabsearch/index.html?uuid=%s'> Ga naar fiche</a>""" % (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 = QtCore.QRegExp("?*", QtCore.Qt.CaseInsensitive, QtCore.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 = metadata.MDdata( self.md.searchAll( self.zoek, themekey, orgName, dataType, siteId, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = metadata.MDdata( self.md.searchAll( self.zoek )) except: self.bar.pushMessage("Error", str( sys.exc_info()[1]), level=QgsMessageBar.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( QtCore.QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QtCore.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.iface.mapCanvas().mapRenderer().destinationCrs().authid() if crs != 'EPSG:31370' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:31370' try: lyrs = metadata.getWmsLayerNames( self.wms , self.proxy, self.port) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WMS", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wms ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.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] url= self.wms.split('?')[0] if crs != 'EPSG:31370' or crs != 'EPSG:3857' : crs = 'EPSG:31370' wmsUrl = "url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % (url, layerName, style , crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): rlayer.renderer().setOpacity(0.8) QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.CRITICAL, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = metadata.getWFSLayerNames( self.wfs, self.proxy, self.port) except: self.bar.pushMessage( "Error", str( sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage("WFS", QtCore.QCoreApplication.translate("geopunt4QgisDataCatalog", "Kan geen lagen vinden in: %s" % self.wfs ), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.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 = metadata.makeWFSuri( url, layerName, crs ) try: vlayer = QgsVectorLayer( wfsUri, layerTitle , "WFS") QgsMapLayerRegistry.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str( sys.exc_info()[1] ), level=QgsMessageBar.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)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) tooltips = self._alg.getParameterDescriptions() self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.name) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.isAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText( self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') label = QLabel(desc) self.labels[param.name] = label widget = self.getWidgetFromParameter(param) self.valueItems[param.name] = widget if param.name in tooltips.keys(): tooltip = tooltips[param.name] else: tooltip = param.description label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.isAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.hidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setLayout(self.verticalLayout) self.setPreviousValues() self.setWindowTitle(self._alg.name) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.tabWidget = QTabWidget() self.tabWidget.setMinimumWidth(300) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.txtHelp = QTextBrowser() html = None isText, algHelp = self._alg.help() if algHelp is not None: algHelp = algHelp if isText else QUrl(algHelp) try: if isText: self.txtHelp.setHtml(algHelp) else: html = self.tr( '<p>Downloading algorithm help... Please wait.</p>') self.txtHelp.setHtml(html) self.reply = QgsNetworkAccessManager.instance().get( QNetworkRequest(algHelp)) self.reply.finished.connect(self.requestFinished) except: self.txtHelp.setHtml( self.tr('<h2>No help available for this algorithm</h2>')) self.tabWidget.addTab(self.txtHelp, 'Help') self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) QMetaObject.connectSlotsByName(self) def requestFinished(self): """Change the webview HTML content""" reply = self.sender() if reply.error() != QNetworkReply.NoError: html = self.tr( '<h2>No help available for this algorithm</h2><p>{}</p>'. format(reply.errorString())) else: html = unicode(reply.readAll()) reply.deleteLater() self.txtHelp.setHtml(html) def getAvailableDependencies(self): if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in self.model.algs.values(): if alg.name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel( [alg.algorithm.name for alg in self.getAvailableDependencies()]) def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.isAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): values = [] inputs = self.model.inputs for i in inputs.values(): param = i.param if isinstance(param, paramType): if dataType is not None and param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in self.model.algs.values(): if alg.name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.name, out.name)) else: values.append(ValueFromOutput(alg.name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description else: alg = self.model.algs[value.alg] return self.tr("'%s' from algorithm '%s'") % ( alg.algorithm.getOutputFromName( value.output).description, alg.description) def getWidgetFromParameter(self, param): if isinstance(param, ParameterRaster): item = QComboBox() layers = self.getAvailableValuesOfType(ParameterRaster, OutputRaster) if param.optional: item.addItem(self.NOT_SELECTED, None) for layer in layers: item.addItem(self.resolveValueDescription(layer), layer) elif isinstance(param, ParameterVector): item = QComboBox() layers = self.getAvailableValuesOfType(ParameterVector, OutputVector) if param.optional: item.addItem(self.NOT_SELECTED, None) for layer in layers: item.addItem(self.resolveValueDescription(layer), layer) elif isinstance(param, ParameterTable): item = QComboBox() tables = self.getAvailableValuesOfType(ParameterTable, OutputTable) layers = self.getAvailableValuesOfType(ParameterVector, OutputVector) if param.optional: item.addItem(self.NOT_SELECTED, None) for table in tables: item.addItem(self.resolveValueDescription(table), table) for layer in layers: item.addItem(self.resolveValueDescription(layer), layer) elif isinstance(param, ParameterBoolean): item = QComboBox() item.addItem('Yes') item.addItem('No') bools = self.getAvailableValuesOfType(ParameterBoolean, None) for b in bools: item.addItem(self.resolveValueDescription(b), b) if param.default: item.setCurrentIndex(0) else: item.setCurrentIndex(1) elif isinstance(param, ParameterSelection): item = QComboBox() item.addItems(param.options) item.setCurrentIndex(param.default or 0) elif isinstance(param, ParameterFixedTable): item = FixedTablePanel(param) elif isinstance(param, ParameterRange): item = RangePanel(param) elif isinstance(param, ParameterMultipleInput): if param.datatype == dataobjects.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) elif param.datatype == dataobjects.TYPE_VECTOR_POINT: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_LINE: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_RASTER: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) else: options = self.getAvailableValuesOfType( ParameterFile, OutputFile) opts = [] for opt in options: opts.append(self.resolveValueDescription(opt)) item = MultipleInputPanel(opts) elif isinstance(param, ParameterString): strings = self.getAvailableValuesOfType(ParameterString, OutputString) options = [(self.resolveValueDescription(s), s) for s in strings] if param.multiline: item = MultilineTextPanel(options) item.setText(unicode(param.default or "")) else: item = QComboBox() item.setEditable(True) for desc, val in options: item.addItem(desc, val) item.setEditText(unicode(param.default or "")) elif isinstance(param, ParameterTableField): item = QComboBox() item.setEditable(True) fields = self.getAvailableValuesOfType(ParameterTableField, None) for f in fields: item.addItem(self.resolveValueDescription(f), f) elif isinstance(param, ParameterTableMultipleField): item = QComboBox() item.setEditable(True) fields = self.getAvailableValuesOfType(ParameterTableMultipleField, None) for f in fields: item.addItem(self.resolveValueDescription(f), f) elif isinstance(param, ParameterNumber): item = QComboBox() item.setEditable(True) numbers = self.getAvailableValuesOfType(ParameterNumber, OutputNumber) for n in numbers: item.addItem(self.resolveValueDescription(n), n) item.setEditText(unicode(param.default)) elif isinstance(param, ParameterCrs): item = QComboBox() values = self.getAvailableValuesOfType(ParameterCrs, OutputCrs) for v in values: item.addItem(self.resolveValueDescription(v), v) elif isinstance(param, ParameterExtent): item = QComboBox() item.setEditable(True) extents = self.getAvailableValuesOfType(ParameterExtent, OutputExtent) if self.canUseAutoExtent(): item.addItem(self.USE_MIN_COVERING_EXTENT, None) for ex in extents: item.addItem(self.resolveValueDescription(ex), ex) if not self.canUseAutoExtent(): item.setEditText(unicode(param.default)) elif isinstance(param, ParameterPoint): item = QComboBox() item.setEditable(True) points = self.getAvailableValuesOfType(ParameterPoint) for p in points: item.addItem(self.resolveValueDescription(p), p) item.setEditText(unicode(param.default)) elif isinstance(param, ParameterFile): item = QComboBox() item.setEditable(True) files = self.getAvailableValuesOfType(ParameterFile, OutputFile) for f in files: item.addItem(self.resolveValueDescription(f), f) elif isinstance(param, ParameterGeometryPredicate): item = GeometryPredicateSelectionPanel(param.enabledPredicates) else: item = QLineEdit() try: item.setText(unicode(param.default)) except: pass return item def canUseAutoExtent(self): for param in self._alg.parameters: if isinstance( param, (ParameterRaster, ParameterVector, ParameterMultipleInput)): return True return False def setTableContent(self): params = self._alg.parameters outputs = self._alg.outputs visibleParams = [p for p in params if not p.hidden] visibleOutputs = [p for o in outputs if not o.hidden] self.tableWidget.setRowCount(len(visibleParams) + len(visibleOutputs)) for i, param in visibleParams: item = QTableWidgetItem(param.description) item.setFlags(Qt.ItemIsEnabled) self.tableWidget.setItem(i, 0, item) item = self.getWidgetFromParameter(param) self.valueItems[param.name] = item self.tableWidget.setCellWidget(i, 1, item) self.tableWidget.setRowHeight(i, 22) for i, output in visibleOutputs: item = QTableWidgetItem(output.description + '<' + output.__module__.split('.')[-1] + '>') item.setFlags(Qt.ItemIsEnabled) self.tableWidget.setItem(i, 0, item) item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.valueItems[output.name] = item self.tableWidget.setCellWidget(i, 1, item) self.tableWidget.setRowHeight(i, 22) def setComboBoxValue(self, combo, value, param): if isinstance(value, list): value = value[0] items = [combo.itemData(i) for i in range(combo.count())] try: idx = items.index(value) combo.setCurrentIndex(idx) return except ValueError: pass if combo.isEditable(): if value is not None: combo.setEditText(unicode(value)) elif isinstance(param, ParameterSelection): combo.setCurrentIndex(int(value)) elif isinstance(param, ParameterBoolean): if value: combo.setCurrentIndex(0) else: combo.setCurrentIndex(1) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameters: if param.hidden: continue widget = self.valueItems[param.name] if param.name in alg.params: value = alg.params[param.name] else: value = param.default if isinstance( param, (ParameterRaster, ParameterVector, ParameterTable, ParameterTableField, ParameterSelection, ParameterNumber, ParameterBoolean, ParameterExtent, ParameterFile, ParameterPoint)): self.setComboBoxValue(widget, value, param) elif isinstance(param, ParameterString): if param.multiline: widget.setValue(value) else: self.setComboBoxValue(widget, value, param) elif isinstance(param, ParameterCrs): widget.setAuthId(value) elif isinstance(param, ParameterFixedTable): pass # TODO! elif isinstance(param, ParameterMultipleInput): if param.datatype == dataobjects.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) elif param.datatype == dataobjects.TYPE_VECTOR_POINT: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_LINE: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_RASTER: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) else: options = self.getAvailableValuesOfType( ParameterFile, OutputFile) selected = [] for i, opt in enumerate(options): if opt in value: selected.append(i) widget.setSelectedItems(selected) elif isinstance(param, ParameterGeometryPredicate): widget.setValue(value) for name, out in alg.outputs.iteritems(): widget = self.valueItems[name].setText(out.description) selected = [] dependencies = self.getAvailableDependencies() for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.commandLineName()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameters outputs = self._alg.outputs for param in params: if param.hidden: continue if not self.setParamValue(alg, param, self.valueItems[param.name]): self.bar.pushMessage( "Error", "Wrong or missing value for parameter '%s'" % param.description, level=QgsMessageBar.WARNING) return None for output in outputs: if not output.hidden: name = unicode(self.valueItems[output.name].text()) if name.strip( ) != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() for selected in selectedOptions: alg.dependencies.append(availableDependencies[selected].name) return alg def setParamValueLayerOrTable(self, alg, param, widget): idx = widget.currentIndex() if idx < 0: return False else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamTableFieldValue(self, alg, param, widget): idx = widget.findText(widget.currentText()) if idx < 0: s = unicode(widget.currentText()).strip() if s == '': if param.optional: alg.params[param.name] = None return True else: return False else: alg.params[param.name] = s return True else: alg.params[param.name] = widget.itemData(widget.currentIndex()) return True def setParamStringValue(self, alg, param, widget): if param.multiline: value = widget.getValue() option = widget.getOption() if option == MultilineTextPanel.USE_TEXT: if value == '': if param.optional: alg.params[param.name] = None return True else: return False else: alg.params[param.name] = value else: alg.params[param.name] = value else: idx = widget.findText(widget.currentText()) if idx < 0: value = widget.currentText().strip() if value == '': if param.optional: alg.params[param.name] = None return True else: return False else: alg.params[param.name] = value else: alg.params[param.name] = widget.itemData(widget.currentIndex()) return True def setParamFileValue(self, alg, param, widget): idx = widget.findText(widget.currentText()) if idx < 0: value = widget.currentText() else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamNumberValue(self, alg, param, widget): idx = widget.findText(widget.currentText()) if idx < 0: s = widget.currentText().strip() if s: try: value = float(s) except: return False elif param.optional: value = None else: return False else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamExtentValue(self, alg, param, widget): idx = widget.findText(widget.currentText()) if idx < 0: s = unicode(widget.currentText()).strip() if s: try: tokens = s.split(',') if len(tokens) != 4: return False for token in tokens: float(token) except: return False elif param.optional: s = None else: return False alg.params[param.name] = [s] else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamPointValue(self, alg, param, widget): idx = widget.findText(widget.currentText()) if idx < 0: s = unicode(widget.currentText()).strip() if s: try: tokens = s.split(',') if len(tokens) != 2: return False for token in tokens: float(token) except: return False elif param.optional: s = None else: return False alg.params[param.name] = [s] else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamCrsValue(self, alg, param, widget): idx = widget.currentIndex() if idx < 0: return False else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True def setParamValue(self, alg, param, widget): if isinstance(param, (ParameterRaster, ParameterVector, ParameterTable)): return self.setParamValueLayerOrTable(alg, param, widget) elif isinstance(param, ParameterBoolean): if widget.currentIndex() < 2: value = widget.currentIndex() == 0 else: value = widget.itemData(widget.currentIndex()) alg.params[param.name] = value return True elif isinstance(param, ParameterString): return self.setParamStringValue(alg, param, widget) elif isinstance(param, ParameterNumber): return self.setParamNumberValue(alg, param, widget) elif isinstance(param, ParameterExtent): return self.setParamExtentValue(alg, param, widget) elif isinstance(param, ParameterPoint): return self.setParamPointValue(alg, param, widget) elif isinstance(param, ParameterFile): return self.setParamFileValue(alg, param, widget) elif isinstance(param, ParameterSelection): alg.params[param.name] = widget.currentIndex() return True elif isinstance(param, ParameterRange): alg.params[param.name] = widget.getValue() return True elif isinstance(param, ParameterCrs): return self.setParamCrsValue(alg, param, widget) elif isinstance(param, ParameterFixedTable): table = widget.table if not bool(table) and not param.optional: return False alg.params[param.name] = ParameterFixedTable.tableToString(table) return True elif isinstance(param, (ParameterTableField, ParameterTableMultipleField)): return self.setParamTableFieldValue(alg, param, widget) elif isinstance(param, ParameterMultipleInput): if param.datatype == dataobjects.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) elif param.datatype == dataobjects.TYPE_VECTOR_POINT: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POINT, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_LINE: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_LINE, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_VECTOR_POLYGON: options = self.getAvailableValuesOfType( ParameterVector, OutputVector, [ dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_ANY ]) elif param.datatype == dataobjects.TYPE_RASTER: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) else: options = self.getAvailableValuesOfType( ParameterFile, OutputFile) values = [options[i] for i in widget.selectedoptions] if len(values) == 0 and not param.optional: return False alg.params[param.name] = values return True elif isinstance(param, ParameterGeometryPredicate): alg.params[param.name] = widget.value() return True else: alg.params[param.name] = unicode(widget.text()) return True def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close()
class EditorTab(QWidget): def __init__(self, parent, parentConsole, filename, *args): QWidget.__init__(self, parent=None, *args) self.mw = parent self.pc = parentConsole self.path = None self.fileExcuteList = {} self.fileExcuteList = dict() self.newEditor = Editor(self) self.newEditor.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.newEditor.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.newEditor.modificationChanged.connect(self.modified) if filename: self.newEditor.setText(open(unicode(filename), "r").read()) self.newEditor.setModified(False) self.path = filename # Creates layout for message bar self.layout = QGridLayout(self.newEditor) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) self.tabLayout = QGridLayout(self) self.tabLayout.setContentsMargins(0, 0, 0, 0) self.tabLayout.addWidget(self.newEditor) self.keyFilter = KeyFilter(parent, self) self.setEventFilter(self.keyFilter) def save(self): if self.path is None: self.path = str(QFileDialog().getSaveFileName(self, "Save file", "*.py", "Script file (*.py)")) # If the user didn't select a file, abort the save operation if len(self.path) == 0: self.path = None return msgText = QCoreApplication.translate('PythonConsole', 'Script was correctly saved.') self.pc.callWidgetMessageBarEditor(msgText) # Rename the original file, if it exists path = unicode(self.path) overwrite = os.path.exists(path) if overwrite: temp_path = path + "~" if os.path.exists(temp_path): os.remove(temp_path) os.rename(path, temp_path) # Save the new contents with open(path, "w") as f: f.write(self.newEditor.text()) if overwrite: os.remove(temp_path) fN = path.split('/')[-1] self.mw.setTabTitle(self, fN) self.mw.setTabToolTip(self.mw.currentIndex(), path) self.newEditor.setModified(False) self.pc.updateTabListScript(path, action='append') self.mw.listObject(self) def modified(self, modified): self.mw.tabModified(self, modified) def close(self): self.mw._removeTab(self, tab2index=True) def setEventFilter(self, filter): self.newEditor.installEventFilter(filter) def newTab(self): self.mw.newTabEditor()
class XTFModelConverterDialog(QDialog, DIALOG_XTF_MODEL_CONVERTER_UI): on_result = pyqtSignal(bool) # whether the tool was run successfully or not def __init__(self, controller, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self._controller = controller self.parent = parent self.logger = Logger() self.app = AppInterface() self.validators = Validators() self._dialog_mode = None self._running_tool = False self.tool_name = QCoreApplication.translate("XTFModelConverterDialog", "XTF Model Converter") # Initialize self.initialize_progress() # Set MessageBar for QDialog self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) # Set connections self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.button(QDialogButtonBox.Ok).setText(QCoreApplication.translate("XTFModelConverterDialog", "Convert")) self.finished.connect(self.finished_slot) self.btn_browse_file_source_xtf.clicked.connect( make_file_selector(self.txt_source_xtf, QCoreApplication.translate("XTFModelConverterDialog", "Select the INTERLIS Transfer File .xtf file you want to convert"), QCoreApplication.translate("XTFModelConverterDialog", 'Transfer file (*.xtf)'))) self.btn_browse_file_target_xtf.clicked.connect( make_save_file_selector(self.txt_target_xtf, QCoreApplication.translate( "XTFModelConverterDialog", "Set the output path of the coverted INTERLIS Transfer File"), QCoreApplication.translate("XTFModelConverterDialog", 'Transfer file (*.xtf)'), extension='.xtf')) self._controller.progress_changed.connect(self.progress.setValue) self.restore_settings() # Set validations file_validator_xtf_in = FileValidator(pattern='*.xtf', allow_non_existing=False) file_validator_xtf_out = FileValidator(pattern='*.xtf', allow_non_existing=True) self.txt_source_xtf.setValidator(file_validator_xtf_in) self.txt_target_xtf.setValidator(file_validator_xtf_out) self.txt_source_xtf.textChanged.connect(self.validators.validate_line_edits) self.txt_target_xtf.textChanged.connect(self.validators.validate_line_edits) self.txt_source_xtf.textChanged.connect(self.update_model_converters) self.txt_source_xtf.textChanged.connect(self.xtf_paths_changed) # Enable/disable convert button self.txt_target_xtf.textChanged.connect(self.xtf_paths_changed) # Enable/disable convert button self.cbo_model_converter.currentIndexChanged.connect(self.selected_converter_changed) # Need new wizard pages? # Trigger validators now self.txt_source_xtf.textChanged.emit(self.txt_source_xtf.text()) self.txt_target_xtf.textChanged.emit(self.txt_target_xtf.text()) def progress_changed(self, value): QCoreApplication.processEvents() # Listen to cancel from the user self.progress.setValue(value) def accepted(self): self.save_settings() self.bar.clearWidgets() # Remove previous messages self.set_gui_controls_enabled(False) self.progress.setVisible(True) msg = QCoreApplication.translate("XTFModelConverterDialog", "Converting XTF data (this might take a while)...") with ProcessWithStatus(msg): params = {} res, msg = self._controller.convert(self.cbo_model_converter.currentData(), self.txt_source_xtf.text(), self.txt_target_xtf.text(), params) self.show_message(msg, Qgis.Success if res else Qgis.Warning) self.logger.success_warning(__name__, res, msg) self.set_gui_controls_enabled(True) def reject(self): if self._running_tool: reply = QMessageBox.question(self, QCoreApplication.translate("XTFModelConverterDialog", "Warning"), QCoreApplication.translate("XTFModelConverterDialog", "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database.").format(self.tool_name), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self._running_tool = False msg = QCoreApplication.translate("XTFModelConverterDialog", "The '{}' tool was cancelled.").format(self.tool_name) self.logger.info(__name__, msg) self.show_message(msg, Qgis.Info) else: self.logger.info(__name__, "Dialog closed.") self.done(1) def finished_slot(self, result): self.bar.clearWidgets() def xtf_paths_changed(self): # Enable/disable 'Convert' button state_source_xtf = self.__source_xtf_is_valid() state_target_xtf = self.txt_target_xtf.validator().validate(self.txt_target_xtf.text().strip(), 0)[0] == QValidator.Acceptable state_converter = self.cbo_model_converter.count() and self.cbo_model_converter.currentData() != "invalid" self.set_convert_button_enabled(state_source_xtf and state_converter and state_target_xtf) def __source_xtf_is_valid(self): return self.txt_source_xtf.validator().validate(self.txt_source_xtf.text().strip(), 0)[0] == QValidator.Acceptable def selected_converter_changed(self, index): # Ideas for this: # Some converters might need new wizard pages. So this slot should get them from the controller # and pass them to a method that shows them, converting first the single-page wizard into multi-page. pass def set_convert_button_enabled(self, enable): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) def update_model_converters(self): self.cbo_model_converter.clear() if self.__source_xtf_is_valid(): self.cbo_model_converter.setEnabled(True) source_xtf = self.txt_source_xtf.text().strip() for key, text in self._controller.get_converters(source_xtf).items(): self.cbo_model_converter.addItem(text, key) if not self.cbo_model_converter.count(): self.cbo_model_converter.addItem(QCoreApplication.translate("XTFModelConverterDialog", "No converter found for the given source XTF"), "invalid") else: if not self.cbo_model_converter.count(): self.cbo_model_converter.setEnabled(False) def initialize_progress(self): self.progress.setValue(0) self.progress.setVisible(False) def set_gui_controls_enabled(self, enable): self.set_convert_button_enabled(enable) self.gbx_parameters.setEnabled(enable) def save_settings(self): settings = QSettings() settings.setValue('Asistente-LADM-COL/xtf_model_converter/xtf_in_path', self.txt_source_xtf.text()) settings.setValue('Asistente-LADM-COL/xtf_model_converter/xtf_out_path', self.txt_target_xtf.text()) # In the main page (source-target configuration), save if splitter is closed self.app.settings.xtf_converter_splitter_collapsed = self.splitter.sizes()[1] == 0 def restore_settings(self): settings = QSettings() self.txt_source_xtf.setText(settings.value('Asistente-LADM-COL/xtf_model_converter/xtf_in_path', '')) self.txt_target_xtf.setText(settings.value('Asistente-LADM-COL/xtf_model_converter/xtf_out_path', '')) # If splitter in the main page was closed before, set it as closed again if self.app.settings.xtf_converter_splitter_collapsed: sizes = self.splitter.sizes() self.splitter.setSizes([sizes[0], 0]) def show_message(self, message, level, duration=0): self.bar.clearWidgets() # Remove previous messages before showing a new one self.bar.pushMessage(message, level, duration)
class ModelerDialog(BASE, WIDGET): CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, alg=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = algList.getAlgorithm(text) if alg is not None: self._addAlgorithm(alg.getCopy(), event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) self.repaintModel() def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): QGraphicsView.mousePressEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.commandLineName()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.fillAlgorithmTree) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) iconSize = settings.value("iconsize", 24) self.mToolbar.setIconSize(QSize(iconSize, iconSize)) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if alg is not None: self.alg = alg self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() else: self.alg = ModelerAlgorithm() self.alg.modelerdialog = self self.fillInputsTree() self.fillAlgorithmTree() self.view.centerOn(0, 0) self.alg.setModelerView(self) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save?'), self.tr('There are unsaved changes in this model, do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.alg.helpContent = dlg.descriptions self.hasChanged = True def runModel(self): if len(self.alg.algs) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=QgsMessageBar.WARNING, duration=5) return if self.alg.provider is None: # Might happen if model is opened from modeler dialog self.alg.provider = QgsApplication.processingRegistry().providerById('model') alg = self.alg.getCopy() dlg = AlgorithmDialog(alg) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('SVG files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.alg.name) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.alg.toPython() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=QgsMessageBar.SUCCESS, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.alg.name = str(self.textName.text()) self.alg.group = str(self.textGroup.text()) if self.alg.descriptionFile is not None and not saveAs: filename = self.alg.descriptionFile else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model)')) if filename: if not filename.endswith('.model'): filename += '.model' self.alg.descriptionFile = filename if filename: text = self.alg.toJson() try: with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) except: if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n %s') % str(sys.exc_info()[1])) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = str(QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model *.MODEL)'))) if filename: try: alg = ModelerAlgorithm.fromFile(filename) self.alg = alg self.alg.setModelerView(self) self.textGroup.setText(alg.group) self.textName.setText(alg.name) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False except WrongModelException as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.msg)) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) except Exception as e: ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, self.tr('Could not load model %s\n%s') % (filename, e.args[0])) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene() self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, ModelerAlgorithm.CANVAS_SIZE)) self.scene.paintModel(self.alg, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.alg, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) self.alg.addParameter(ModelerParameter(dlg.param, pos)) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.inputs: maxX = max([i.pos.x() for i in list(self.alg.inputs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = algList.getAlgorithm(item.alg.commandLineName()) self._addAlgorithm(alg.getCopy()) def _addAlgorithm(self, alg, pos=None): dlg = alg.getCustomModelerParametersDialog(self.alg) if not dlg: dlg = ModelerParametersDialog(alg, self.alg) dlg.exec_() if dlg.alg is not None: if pos is None: dlg.alg.pos = self.getPositionForAlgorithmItem() else: dlg.alg.pos = pos if isinstance(dlg.alg.pos, QPoint): dlg.alg.pos = QPointF(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(dlg.alg.outputs): dlg.alg.outputs[out].pos = dlg.alg.pos + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT) self.alg.addAlgorithm(dlg.alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.alg.algs: maxX = max([alg.pos.x() for alg in list(self.alg.algs.values())]) maxY = max([alg.pos.y() for alg in list(self.alg.algs.values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillAlgorithmTree(self): self.fillAlgorithmTreeUsingProviders() self.algorithmTree.sortItems(0, Qt.AscendingOrder) text = str(self.searchBox.text()) if text != '': self.algorithmTree.expandAll() def fillAlgorithmTreeUsingProviders(self): self.algorithmTree.clear() text = str(self.searchBox.text()) search_strings = text.split(' ') allAlgs = algList.algs for provider_id in list(allAlgs.keys()): name = 'ACTIVATE_' + provider_id.upper().replace(' ', '_') if not ProcessingConfig.getSetting(name): continue groups = {} algs = list(allAlgs[provider_id].values()) # Add algorithms for alg in algs: if not alg.showInModeler: continue if alg.commandLineName() == self.alg.commandLineName(): continue item_text = [alg.name.lower()] item_text.extend(alg.tags.split(',')) show = not search_strings or all( any(part in t for t in item_text) for part in search_strings) if show: if alg.group in groups: groupItem = groups[alg.group] else: groupItem = QTreeWidgetItem() name = alg.i18n_group or alg.group groupItem.setText(0, name) groupItem.setToolTip(0, name) groups[alg.group] = groupItem algItem = TreeAlgorithmItem(alg) groupItem.addChild(algItem) if len(groups) > 0: providerItem = QTreeWidgetItem() provider = QgsApplication.processingRegistry().providerById(provider_id) providerItem.setText(0, provider.name()) providerItem.setToolTip(0, provider.name()) providerItem.setIcon(0, provider.icon()) for groupItem in list(groups.values()): providerItem.addChild(groupItem) self.algorithmTree.addTopLevelItem(providerItem) providerItem.setExpanded(text != '') for groupItem in list(groups.values()): if text != '': groupItem.setExpanded(True) self.algorithmTree.sortItems(0, Qt.AscendingOrder)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.propertiesDock.setWindowTitle(self.tr("Model properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth( self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView( None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip( self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) #self.mActionExportPython.setIcon( # QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState( settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry( settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data( 'application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm_id) if alg is not None: self._addAlgorithm(alg, event.pos()) else: assert False, algorithm_id elif event.mimeData().hasText(): itemId = event.mimeData().text() if itemId in [ param.id() for param in QgsApplication.instance(). processingRegistry().parameterTypes() ]: self.addInputOfType(itemId, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat( 'application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue( self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue( self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel( self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) self.algorithmTree.setFilters( QgsProcessingToolboxProxyModel.FilterModeler) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText( QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self. tr('There are unsaved changes in this model. Do you want to keep those?' ), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage( "", self. tr("Model doesn't contain any algorithm and/or parameter and can't be executed" ), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById( PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene( QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", self.tr("Model was correctly exported as image"), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Model was correctly exported as PDF"), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName( self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Model was correctly exported as SVG"), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage( "", self.tr("Model was correctly exported as python script"), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving')) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName( self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning( self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format( str(sys.exc_info()[1]))) else: QMessageBox.warning( self, self.tr("Can't save model"), QCoreApplication. translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option." ))) return self.update_model.emit() self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName( self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider( QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage( self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical( self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect( QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([ i.position().x() for i in list(self.model.parameterComponents().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted( QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById( algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition( alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([ alg.position().x() for alg in list(self.model.childAlgorithms().values()) ]) maxY = max([ alg.position().y() for alg in list(self.model.childAlgorithms().values()) ]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY)
class SymbologySharingDialog(QtGui.QDialog, FORM_CLASS): TAB_ALL = 0 TAB_INSTALLED = 1 TAB_SETTINGS = 2 def __init__(self, parent=None, iface=None): """Constructor. :param parent: Optional widget to use as parent :type parent: QWidget :param iface: An instance of QGisInterface :type iface: QGisInterface """ super(SymbologySharingDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.repository_manager = RepositoryManager() # Init the message bar self.message_bar = QgsMessageBar(self) self.message_bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.vlayoutRightColumn.insertWidget(0, self.message_bar) # Mock plugin manager dialog self.resize(796, 594) self.setMinimumSize(QSize(790, 0)) self.setModal(True) self.button_edit.setEnabled(False) self.button_delete.setEnabled(False) # Set QListWidgetItem # All icon_all = QIcon() icon_all.addFile( resources_path('img', 'plugin.svg'), QSize(), QIcon.Normal, QIcon.Off) item_all = QListWidgetItem() item_all.setIcon(icon_all) item_all.setText(self.tr('All')) # Installed icon_installed = QIcon() icon_installed.addFile( resources_path('img', 'plugin-installed.svg'), QSize(), QIcon.Normal, QIcon.Off) item_installed = QListWidgetItem() item_installed.setIcon(icon_installed) item_installed.setText(self.tr('Installed')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Settings icon_settings = QIcon() icon_settings.addFile( resources_path('img', 'settings.svg'), QSize(), QIcon.Normal, QIcon.Off) item_settings = QListWidgetItem() item_settings.setIcon(icon_settings) item_settings.setText(self.tr('Settings')) item_all.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) # Add the list widget item to the widget self.menu_list_widget.addItem(item_all) self.menu_list_widget.addItem(item_installed) self.menu_list_widget.addItem(item_settings) # Slots self.button_add.clicked.connect(self.add_repository) self.button_edit.clicked.connect(self.edit_repository) self.button_delete.clicked.connect(self.delete_repository) self.menu_list_widget.currentRowChanged.connect(self.set_current_tab) # Creating progress dialog for downloading stuffs self.progress_dialog = QProgressDialog(self) self.progress_dialog.setAutoClose(False) title = self.tr('Symbology Sharing') self.progress_dialog.setWindowTitle(title) # Populate tree repositories with registered repositories self.populate_tree_repositories() def set_current_tab(self, index): """Set stacked widget based on active tab. :param index: The index of the active list widget item. :type index: int """ if index == (self.menu_list_widget.count() - 1): # Switch to settings tab self.stacked_menu_widget.setCurrentIndex(1) else: # Switch to plugins tab self.stacked_menu_widget.setCurrentIndex(0) def add_repository(self): """Open add repository dialog.""" dlg = ManageRepositoryDialog(self) if not dlg.exec_(): return for repo in self.repository_manager.repositories.values(): if dlg.line_edit_url.text().strip() == repo['url']: self.message_bar.pushMessage( self.tr( 'Unable to add another repository with the same URL!'), QgsMessageBar.WARNING, 5) return repo_name = dlg.line_edit_name.text() repo_url = dlg.line_edit_url.text().strip() if repo_name in self.repository_manager.repositories: repo_name += '(2)' settings = QSettings() settings.beginGroup(repo_settings_group()) settings.setValue(repo_name + '/url', repo_url) # Fetch metadata #TODO: Wrap RemoteRepository class into RepositoryManager # This dialod will only need to call RepositoryManager. # RepositoryManager will take care of the rest remote_repository = RemoteRepository(repo_url) remote_repository.fetch_metadata(self.progress_dialog) # Show metadata #TODO: Process this instead of showing it on message box :) QMessageBox.information( self, self.tr("Test"), remote_repository.metadata.data()) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(False) def edit_repository(self): """Open edit repository dialog.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return dlg = ManageRepositoryDialog(self) dlg.line_edit_name.setText(repo_name) dlg.line_edit_url.setText( self.repository_manager.repositories[repo_name]['url']) if not dlg.exec_(): return # Check if the changed URL is already there in the repo new_url = dlg.line_edit_url.text().strip() old_url = self.repository_manager.repositories[repo_name]['url'] for repo in self.repository_manager.repositories.values(): if new_url == repo['url'] and (old_url != new_url): self.message_bar.pushMessage( self.tr( 'Unable to add another repository with the same URL!'), QgsMessageBar.WARNING, 5) return # Delete old repo and create a new entry settings = QSettings() settings.beginGroup(repo_settings_group()) settings.remove(repo_name) new_name = dlg.line_edit_name.text() if new_name in self.repository_manager.repositories and new_name != repo_name: new_name += '(2)' settings.setValue(new_name + '/url', new_url) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(False) def delete_repository(self): """Delete a repository in the tree widget.""" selected_item = self.tree_repositories.currentItem() if selected_item: repo_name = selected_item.text(0) if not repo_name: return # Check if it's the official repository settings = QSettings() settings.beginGroup(repo_settings_group()) if settings.value(repo_name + '/url') == self.repository_manager.OFFICIAL_REPO[1]: self.message_bar.pushMessage( self.tr( 'You can not remove the official repository!'), QgsMessageBar.WARNING, 5) return warning = self.tr('Are you sure you want to remove the following ' 'repository?') + '\n' + repo_name if QMessageBox.warning( self, self.tr("QGIS Symbology Sharing"), warning, QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return settings.remove(repo_name) # Refresh tree repository self.refresh_tree_repositories() self.set_enabled_edit_delete_button(False) def refresh_tree_repositories(self): """Refresh tree repositories using new repositories data.""" self.repository_manager.load() self.populate_tree_repositories() def populate_tree_repositories(self): """Populate dictionary repositories to the tree widget.""" # Clear the current tree widget self.tree_repositories.clear() # Export the updated ones from the repository manager for repo_name in self.repository_manager.repositories: url = self.repository_manager.repositories[repo_name]['url'] item = QTreeWidgetItem(self.tree_repositories) item.setText(0, repo_name) item.setText(1, url) self.tree_repositories.resizeColumnToContents(0) self.tree_repositories.resizeColumnToContents(1) self.tree_repositories.sortItems(1, Qt.AscendingOrder) def on_tree_repositories_itemSelectionChanged(self): """Slot for when the itemSelectionChanged signal emitted.""" # Activate edit and delete button self.set_enabled_edit_delete_button(True) def set_enabled_edit_delete_button(self, is_enabled): """Disable edit and delete button. :param is_enabled: Boolean is enabled or not. :type is_enabled: bool """ self.button_edit.setEnabled(is_enabled) self.button_delete.setEnabled(is_enabled)
class ShellOutputScintilla(QsciScintilla): def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(10) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setMarginsForegroundColor(QColor("#3E3EE3")) self.setMarginsBackgroundColor(QColor("#f9f9f9")) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemeted copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate( "PythonConsole", "Python Console \n" "Use iface to access QGIS API interface or Type help(iface) for more info" ) ## some translation string for the console header ends without '\n' ## and the first command in console will be appended at the header text. ## The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor("#fcf3ed")) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(Qt.black)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() loadFont = self.settings.value("pythonConsole/fontfamilytext", "Monospace") fontSize = self.settings.value("pythonConsole/fontsize", 10, type=int) font = QFont(loadFont) font.setFixedPitch(True) font.setPointSize(fontSize) font.setStyleHint(QFont.TypeWriter) font.setStretch(QFont.SemiCondensed) font.setLetterSpacing(QFont.PercentageSpacing, 87.0) font.setBold(False) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor( self.settings.value("pythonConsole/defaultFontColor", QColor(Qt.black)))) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentFontColor", QColor(Qt.gray))), 1) self.lexer.setColor( QColor( self.settings.value("pythonConsole/keywordFontColor", QColor(Qt.darkGreen))), 5) self.lexer.setColor( QColor( self.settings.value("pythonConsole/classFontColor", QColor(Qt.blue))), 8) self.lexer.setColor( QColor( self.settings.value("pythonConsole/methodFontColor", QColor(Qt.darkGray))), 9) self.lexer.setColor( QColor( self.settings.value("pythonConsole/decorFontColor", QColor(Qt.darkBlue))), 15) self.lexer.setColor( QColor( self.settings.value("pythonConsole/commentBlockFontColor", QColor(Qt.gray))), 12) self.lexer.setColor( QColor( self.settings.value("pythonConsole/singleQuoteFontColor", QColor(Qt.blue))), 4) self.lexer.setColor( QColor( self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(Qt.blue))), 3) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(Qt.blue))), 6) self.lexer.setColor( QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(Qt.blue))), 7) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(Qt.white))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/iconRunConsole.png") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.png") iconHideTool = QgsApplication.getThemeIcon( "console/iconHideToolConsole.png") iconSettings = QgsApplication.getThemeIcon( "console/iconSettingsConsole.png") menu.addAction( iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction( iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction( iconClear, QCoreApplication.translate("PythonConsole", "Clear console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) menu.addSeparator() selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Settings"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = unicode(self.selectedText()) text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: self.emit(SIGNAL("keyboard_interrupt()")) def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, QgsMessageBar.INFO, timeout)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self._algName = algName self.setupUi() self.params = None def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) tooltips = self._alg.getParameterDescriptions() self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.name) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameters: if param.isAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameters: if param.hidden: continue desc = param.description if isinstance(param, ParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, ParameterPoint): desc += self.tr('(x, y)') if param.optional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name] = label wrapper = param.wrapper(self) self.wrappers[param.name] = wrapper widget = wrapper.widget if widget: self.valueItems[param.name] = widget if param.name in list(tooltips.keys()): tooltip = tooltips[param.name] else: tooltip = param.description label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.isAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for output in self._alg.outputs: if output.hidden: continue if isinstance(output, (OutputRaster, OutputVector, OutputTable, OutputHTML, OutputFile, OutputDirectory)): label = QLabel(output.description + '<' + output.__class__.__name__ + '>') item = QLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[output.name] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.name) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.tabWidget = QTabWidget() self.tabWidget.setMinimumWidth(300) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.tabWidget.addTab(self.scrollArea, self.tr('Parameters')) self.txtHelp = QTextBrowser() html = None isText, algHelp = self._alg.help() if algHelp is not None: algHelp = algHelp if isText else QUrl(algHelp) try: if isText: self.txtHelp.setHtml(algHelp) else: html = self.tr('<p>Downloading algorithm help... Please wait.</p>') self.txtHelp.setHtml(html) self.tabWidget.addTab(self.txtHelp, 'Help') self.reply = QgsNetworkAccessManager.instance().get(QNetworkRequest(algHelp)) self.reply.finished.connect(self.requestFinished) except: pass self.verticalLayout2.addWidget(self.tabWidget) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def requestFinished(self): """Change the webview HTML content""" reply = self.sender() if reply.error() != QNetworkReply.NoError: html = self.tr('<h2>No help available for this algorithm</h2><p>{}</p>'.format(reply.errorString())) else: html = str(reply.readAll()) reply.deleteLater() self.txtHelp.setHtml(html) def getAvailableDependencies(self): if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) opts = [] for alg in list(self.model.algs.values()): if alg.name not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description for alg in self.getAvailableDependencies()]) def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameters: if param.isAdvanced: self.labels[param.name].setVisible(self.showAdvanced) self.widgets[param.name].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outType=None, dataType=None): # upgrade paramType to list if type(paramType) is not list: paramType = [paramType] values = [] inputs = self.model.inputs for i in list(inputs.values()): param = i.param for t in paramType: if isinstance(param, t): if dataType is not None: if param.datatype in dataType: values.append(ValueFromInput(param.name)) else: values.append(ValueFromInput(param.name)) break if outType is None: return values if self._algName is None: dependent = [] else: dependent = self.model.getDependentAlgorithms(self._algName) for alg in list(self.model.algs.values()): if alg.name not in dependent: for out in alg.algorithm.outputs: if isinstance(out, outType): if dataType is not None and out.datatype in dataType: values.append(ValueFromOutput(alg.name, out.name)) else: values.append(ValueFromOutput(alg.name, out.name)) return values def resolveValueDescription(self, value): if isinstance(value, ValueFromInput): return self.model.inputs[value.name].param.description else: alg = self.model.algs[value.alg] return self.tr("'%s' from algorithm '%s'") % (alg.algorithm.getOutputFromName(value.output).description, alg.description) def setPreviousValues(self): if self._algName is not None: alg = self.model.algs[self._algName] self.descriptionBox.setText(alg.description) for param in alg.algorithm.parameters: if param.hidden: continue if param.name in alg.params: value = alg.params[param.name] else: value = param.default self.wrappers[param.name].setValue(value) for name, out in list(alg.outputs.items()): self.valueItems[name].setText(out.description) selected = [] dependencies = self.getAvailableDependencies() for idx, dependency in enumerate(dependencies): if dependency.name in alg.dependencies: selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = Algorithm(self._alg.commandLineName()) alg.setName(self.model) alg.description = self.descriptionBox.text() params = self._alg.parameters outputs = self._alg.outputs for param in params: if param.hidden: continue if not self.setParamValue(alg, param, self.wrappers[param.name]): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description, level=QgsMessageBar.WARNING) return None for output in outputs: if not output.hidden: name = str(self.valueItems[output.name].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: alg.outputs[output.name] = ModelerOutput(name) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() for selected in selectedOptions: alg.dependencies.append(availableDependencies[selected].name) self._alg.processBeforeAddingToModeler(alg, self.model) return alg def setParamValue(self, alg, param, wrapper): try: if wrapper.widget: value = wrapper.value() alg.params[param.name] = value return True except InvalidParameterValue: return False def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close()
class dataCatalog(QtGui.QDialog): def __init__(self, iface): QtGui.QDialog.__init__(self, None) self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) #self.setWindowFlags( self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QtCore.QSettings().value("locale/userLocale", "nl") locale = locale[0:2] localePath = os.path.join(os.path.dirname(__file__), 'i18n', '{}.qm'.format(locale)) if os.path.exists(localePath): self.translator = QtCore.QTranslator() self.translator.load(localePath) if QtCore.qVersion() > '4.3.3': QtCore.QCoreApplication.installTranslator(self.translator) self._initGui() def _initGui(self): """setup the user interface""" self.ui = Ui_dataCatalogDlg() self.ui.setupUi(self) #get settings self.s = settings() self.gh = gh.geometryHelper(self.iface) #setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) #vars self.firstShow = True self.wms = None self.wfs = None self.wmts = None self.wcs = None self.dl = None self.zoek = '' #datamodel self.model = QtGui.QStandardItemModel(self) self.proxyModel = QtGui.QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel(self.proxyModel) #completer self.completer = QtGui.QCompleter(self) self.completerModel = QtGui.QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer) self.completer.setModel(self.completerModel) self.md = metadata.MDReader(timeout=self.s.timeout, proxyUrl=self.s.proxyUrl) #eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.addWMTSbtn.clicked.connect(self.addWMTS) self.ui.addWCSbtn.clicked.connect(self.addWCS) 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.finished.connect(self.clean) def _setModel(self, records): self.model.clear() reclist = sorted(records, key=lambda k: k['title']) for rec in reclist: title = QtGui.QStandardItem(rec['title']) #0 wms = QtGui.QStandardItem(rec['wms'][1]) #1 downloadLink = QtGui.QStandardItem(rec['download']) #2 id = QtGui.QStandardItem(rec['uuid']) #3 abstract = QtGui.QStandardItem(rec['abstract']) #4 wfs = QtGui.QStandardItem(rec['wfs'][1]) #5 wcs = QtGui.QStandardItem(rec['wcs'][1]) #6 wmts = QtGui.QStandardItem(rec['wmts'][1]) #7 ### wmsName = QtGui.QStandardItem(rec['wms'][0]) #8 wfsName = QtGui.QStandardItem(rec['wfs'][0]) #9 wcsName = QtGui.QStandardItem(rec['wcs'][0]) #10 wmtsName = QtGui.QStandardItem(rec['wmts'][0]) #11 self.model.appendRow([ title, wms, downloadLink, id, abstract, wfs, wcs, wmts, wmsName, wfsName, wcsName, wmtsName ]) #overwrite def show(self): QtGui.QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on(self.s.proxyUrl) if inet: 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.keywordCbx.addItems( ['']+ keywords ) 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 else: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate( "datacatalog", "Kan geen verbing maken met het internet."), level=QgsMessageBar.WARNING, duration=3) #eventhandlers def resultViewClicked(self): if self.ui.resultView.selectedIndexes(): row = self.ui.resultView.selectedIndexes()[0].row() 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)) self.wcs = self.proxyModel.data(self.proxyModel.index(row, 6)) self.wmts = self.proxyModel.data(self.proxyModel.index(row, 7)) ## self.wmsLr = self.proxyModel.data(self.proxyModel.index(row, 8)) self.wfsLr = self.proxyModel.data(self.proxyModel.index(row, 9)) self.wcsLr = self.proxyModel.data(self.proxyModel.index(row, 10)) self.wmtsLr = self.proxyModel.data(self.proxyModel.index(row, 11)) ## title = self.proxyModel.data(self.proxyModel.index(row, 0)) 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/><br/> <a href='http://www.nationaalgeoregister.nl/geonetwork/srv/search/?uuid=%s'> Bekijk in Nationaal Georegister</a>""" % (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.wmts: self.ui.addWMTSbtn.setEnabled(1) else: self.ui.addWMTSbtn.setEnabled(0) if self.wcs: self.ui.addWCSbtn.setEnabled(1) else: self.ui.addWCSbtn.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) elif value == 4: self.filterModel(6) elif value == 5: self.filterModel(7) else: self.filterModel() def filterModel(self, col=None): if col != None: self.proxyModel.setFilterKeyColumn(col) expr = QtCore.QRegExp("?*", QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard) self.proxyModel.setFilterRegExp(expr) else: self.proxyModel.setFilterRegExp(None) def search(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) if self.ui.filterBox.isChecked(): 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 ='' #keyword = self.ui.keywordCbx.currentText() inspiretheme = self.ui.INSPIREthemaCbx.currentText() inspireannex = self.ui.INSPIREannexCbx.currentText() inspireServiceType = self.ui.INSPIREserviceCbx.currentText() searchResult = metadata.MDdata( self.md.searchAll(self.zoek, orgName, dataType, None, inspiretheme, inspireannex, inspireServiceType)) else: searchResult = metadata.MDdata(self.md.searchAll(self.zoek)) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=3) return finally: QtGui.QApplication.restoreOverrideCursor() self.ui.countLbl.setText("Aantal gevonden: %s" % searchResult.count) self.ui.descriptionText.setText('') self._setModel(searchResult.records) if searchResult.count == 0: self.bar.pushMessage( QtCore.QCoreApplication.translate("datacatalog", "Waarschuwing "), QtCore.QCoreApplication.translate( "datacatalog", "Er zijn geen resultaten gevonden 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:28992' or crs != 'EPSG:3857' or crs != 'EPSG:3043': crs = 'EPSG:28992' try: lyrs = metadata.getWmsLayerNames(self.wms, self.s.proxyUrl) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage( "WMS", QtCore.QCoreApplication.translate( "datacatalog", "Kan geen lagen vinden in: %s" % self.wms), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.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] url = self.wms.split('?')[0] if crs != 'EPSG:28992' or crs != 'EPSG:3857': crs = 'EPSG:28992' wmsUrl = "url=%s&layers=%s&format=image/png&styles=%s&crs=%s" % ( url, layerName, style, crs) try: rlayer = QgsRasterLayer(wmsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return def addWFS(self): if self.wfs == None: return try: lyrs = metadata.getWFSLayerNames(self.wfs, self.s.proxyUrl) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage( "WFS", QtCore.QCoreApplication.translate( "datacatalog", "Kan geen lagen vinden in: %s" % self.wfs), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.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] if self.ui.wfsBboxchk.isChecked(): extent = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs( [extent.xMinimum(), extent.yMinimum()], int(crs.split(":")[-1])) maxX, maxY = self.gh.prjPtFromMapCrs( [extent.xMaximum(), extent.yMaximum()], int(crs.split(":")[-1])) bbox = [minX, minY, maxX, maxY] else: bbox = None wfsUri = metadata.makeWFSuri(url, layerName, crs, bbox=bbox) try: vlayer = QgsVectorLayer(wfsUri, layerTitle, "WFS") QgsMapLayerRegistry.instance().addMapLayer(vlayer) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return def addWMTS(self): if self.wmts == None: return try: lyrs = metadata.getWMTSlayersNames(self.wmts, self.s.proxyUrl) except: self.bar.pushMessage("Error", 'Kan niet connecteren met ' + self.wmts, level=QgsMessageBar.CRITICAL, duration=10) return if len(lyrs) == 0: self.bar.pushMessage( "WMTS", QtCore.QCoreApplication.translate( "datacatalog", "Kan geen lagen vinden in: %s" % self.wmts), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem( self, "WMTS 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] matrix = [n[2] for n in lyrs if n[1] == layerTitle][0] frmt = [n[3] for n in lyrs if n[1] == layerTitle][0] srs = [n[4] for n in lyrs if n[1] == layerTitle][0] wmtsUrl = metadata.makeWMTSuri(self.wmts, layerName, matrix, format=frmt, srsname=srs) try: rlayer = QgsRasterLayer(wmtsUrl, layerTitle, 'wms') if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "datacatalog", "Kan WMS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return def addWCS(self): try: QtGui.QApplication.setOverrideCursor( QtGui.QCursor(QtCore.Qt.WaitCursor)) lyrs = metadata.getWCSlayerNames(self.wcs, self.s.proxyUrl) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return finally: QtGui.QApplication.restoreOverrideCursor() if len(lyrs) == 0: self.bar.pushMessage( "WCS", QtCore.QCoreApplication.translate( "datacatalog", "Kan geen lagen vinden in: %s" % self.wcs), level=QgsMessageBar.WARNING, duration=10) return elif len(lyrs) == 1: layerTitle = lyrs[0][1] else: layerTitle, accept = QtGui.QInputDialog.getItem( self, "WCS 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] layerFormat = [n[2] for n in lyrs if n[1] == layerTitle][0] srs = [n[3] for n in lyrs if n[1] == layerTitle][0] axis = [n[4] for n in lyrs if n[1] == layerTitle][0] wcsUri = metadata.makeWCSuri(self.wcs, layerName, srsname=srs, invertAxis=axis) try: rlayer = QgsRasterLayer(wcsUri, layerTitle, "wcs") if rlayer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(rlayer) if rlayer.bandCount() > 1: rlayer.setDrawingStyle("MultiBandSingleBandGray") else: self.bar.pushMessage("Error", QtCore.QCoreApplication.translate( "datacatalog", "Kan WCS niet laden"), level=QgsMessageBar.CRITICAL, duration=10) except: self.bar.pushMessage("Error", str(sys.exc_info()[1]), level=QgsMessageBar.CRITICAL, duration=10) return def clean(self): self.model.clear() self.wms = None self.wfs = None self.dl = None self.wmts = None self.wcs = 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.addWMTSbtn.setEnabled(0) self.ui.addWMSbtn.setEnabled(0) self.ui.addWCSbtn.setEnabled(0) self.ui.modelFilterCbx.setCurrentIndex(0)
class ShellOutputScintilla(QsciScintilla): DEFAULT_COLOR = "#4d4d4c" KEYWORD_COLOR = "#8959a8" CLASS_COLOR = "#4271ae" METHOD_COLOR = "#4271ae" DECORATION_COLOR = "#3e999f" NUMBER_COLOR = "#c82829" COMMENT_COLOR = "#8e908c" COMMENT_BLOCK_COLOR = "#8e908c" BACKGROUND_COLOR = "#ffffff" CURSOR_COLOR = "#636363" CARET_LINE_COLOR = "#efefef" SINGLE_QUOTE_COLOR = "#718c00" DOUBLE_QUOTE_COLOR = "#718c00" TRIPLE_SINGLE_QUOTE_COLOR = "#eab700" TRIPLE_DOUBLE_QUOTE_COLOR = "#eab700" MARGIN_BACKGROUND_COLOR = "#efefef" MARGIN_FOREGROUND_COLOR = "#636363" SELECTION_BACKGROUND_COLOR = "#d7d7d7" SELECTION_FOREGROUND_COLOR = "#303030" MATCHED_BRACE_BACKGROUND_COLOR = "#b7f907" MATCHED_BRACE_FOREGROUND_COLOR = "#303030" def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) #fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can lead to data loss and/or leak") # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() self.setSelectionForegroundColor(QColor(self.settings.value("pythonConsole/selectionForegroundColor", QColor(self.SELECTION_FOREGROUND_COLOR)))) self.setSelectionBackgroundColor(QColor(self.settings.value("pythonConsole/selectionBackgroundColor", QColor(self.SELECTION_BACKGROUND_COLOR)))) self.setMarginsForegroundColor(QColor(self.settings.value("pythonConsole/marginForegroundColor", QColor(self.MARGIN_FOREGROUND_COLOR)))) self.setMarginsBackgroundColor(QColor(self.settings.value("pythonConsole/marginBackgroundColor", QColor(self.MARGIN_BACKGROUND_COLOR)))) caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor(self.CARET_LINE_COLOR)) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(self.CURSOR_COLOR)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor(QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(self.DEFAULT_COLOR)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(self.COMMENT_COLOR))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/numberFontColor", QColor(self.NUMBER_COLOR))), 2) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(self.KEYWORD_COLOR))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(self.CLASS_COLOR))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(self.METHOD_COLOR))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(self.DECORATION_COLOR))), 15) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(self.COMMENT_BLOCK_COLOR))), 12) self.lexer.setColor(QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(self.SINGLE_QUOTE_COLOR))), 4) self.lexer.setColor(QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(self.DOUBLE_QUOTE_COLOR))), 3) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(self.TRIPLE_SINGLE_QUOTE_COLOR))), 6) self.lexer.setColor(QColor(self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(self.TRIPLE_DOUBLE_QUOTE_COLOR))), 7) self.lexer.setColor(QColor(self.settings.value("pythonConsole/defaultFontColorEditor", QColor(self.DEFAULT_COLOR))), 13) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.lexer.setFont(font, QsciLexerPython.UnclosedString) for style in range(0, 33): paperColor = QColor(self.settings.value("pythonConsole/paperBackgroundColor", QColor(self.BACKGROUND_COLOR))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.svg") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.svg") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.svg") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.mToolbar.setIconSize(iface.iconSize()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.mActionExportPython.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = QgsApplication.processingRegistry().createAlgorithmById(text) if alg is not None: self._addAlgorithm(alg, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.id()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(self.tr('Search...')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.textChanged) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.fillInputsTree() self.fillTreeUsingProviders() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=QgsMessageBar.WARNING, duration=5) return dlg = AlgorithmDialog(self.model) dlg.exec_() # have to manually delete the dialog - otherwise it's owned by the # iface mainWindow and never deleted dlg.deleteLater() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=QgsMessageBar.SUCCESS, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), self.tr("This model can't be saved in its " "original location (probably you do not " "have permission to do it). Please, use " "the 'Save as...' option.")) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=QgsMessageBar.SUCCESS, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), QgsMessageLog.CRITICAL) QMessageBox.critical(self, self.tr('Could not open model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def textChanged(self): text = self.searchBox.text().strip(' ').lower() for item in list(self.disabledProviderItems.values()): item.setHidden(True) self._filterItem(self.algorithmTree.invisibleRootItem(), [t for t in text.split(' ') if t]) if text: self.algorithmTree.expandAll() self.disabledWithMatchingAlgs = [] for provider in QgsApplication.processingRegistry().providers(): if not provider.isActive(): for alg in provider.algorithms(): if text in alg.name(): self.disabledWithMatchingAlgs.append(provider.id()) break else: self.algorithmTree.collapseAll() def _filterItem(self, item, text): if (item.childCount() > 0): show = False for i in range(item.childCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) and item not in list(self.disabledProviderItems.values()) item.setHidden(not show) return show elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)): # hide if every part of text is not contained somewhere in either the item text or item user role item_text = [item.text(0).lower(), item.data(0, ModelerDialog.NAME_ROLE).lower()] if isinstance(item, TreeAlgorithmItem): item_text.append(item.alg.id()) item_text.extend(item.data(0, ModelerDialog.TAG_ROLE)) hide = bool(text) and not all( any(part in t for t in item_text) for part in text) item.setHidden(hide) return not hide else: item.setHidden(True) return False def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in ModelerParameterDefinitionDialog.paramTypes: paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = QgsApplication.processingRegistry().createAlgorithmById(item.alg.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = None try: dlg = alg.getCustomModelerParametersDialog(self.model) except: pass if not dlg: dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillTreeUsingProviders(self): self.algorithmTree.clear() self.disabledProviderItems = {} # TODO - replace with proper model for toolbox! # first add qgis/native providers, since they create top level groups for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): self.addAlgorithmsFromProvider(provider, self.algorithmTree.invisibleRootItem()) else: continue self.algorithmTree.sortItems(0, Qt.AscendingOrder) for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): # already added continue else: providerItem = TreeProviderItem(provider, self.algorithmTree, self) if not provider.isActive(): providerItem.setHidden(True) self.disabledProviderItems[provider.id()] = providerItem # insert non-native providers at end of tree, alphabetically for i in range(self.algorithmTree.invisibleRootItem().childCount()): child = self.algorithmTree.invisibleRootItem().child(i) if isinstance(child, TreeProviderItem): if child.text(0) > providerItem.text(0): break self.algorithmTree.insertTopLevelItem(i + 1, providerItem) def addAlgorithmsFromProvider(self, provider, parent): groups = {} count = 0 algs = provider.algorithms() active = provider.isActive() # Add algorithms for alg in algs: if alg.flags() & QgsProcessingAlgorithm.FlagHideFromToolbox: continue groupItem = None if alg.group() in groups: groupItem = groups[alg.group()] else: # check if group already exists for i in range(parent.childCount()): if parent.child(i).text(0) == alg.group(): groupItem = parent.child(i) groups[alg.group()] = groupItem break if not groupItem: groupItem = TreeGroupItem(alg.group()) if not active: groupItem.setInactive() if provider.id() in ('qgis', 'native', '3d'): groupItem.setIcon(0, provider.icon()) groups[alg.group()] = groupItem algItem = TreeAlgorithmItem(alg) if not active: algItem.setForeground(0, Qt.darkGray) groupItem.addChild(algItem) count += 1 text = provider.name() if not provider.id() in ('qgis', 'native', '3d'): if not active: def activateProvider(): self.activateProvider(provider.id()) label = QLabel(text + " <a href='%s'>Activate</a>") label.setStyleSheet("QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) self.algorithmTree.setItemWidget(parent, 0, label) else: parent.setText(0, text) for group, groupItem in sorted(groups.items(), key=operator.itemgetter(1)): parent.addChild(groupItem) if not provider.id() in ('qgis', 'native', '3d'): parent.setHidden(parent.childCount() == 0)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super(ModelerDialog, self).__init__(None) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass self.mToolbar.setIconSize(iface.iconSize()) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) #self.mActionExportPython.setIcon( # QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.zoom = 1 self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) def _dragEnterEvent(event): if event.mimeData().hasText(): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): if event.mimeData().hasText(): text = event.mimeData().text() if text in ModelerParameterDefinitionDialog.paramTypes: self.addInputOfType(text, event.pos()) else: alg = QgsApplication.processingRegistry().createAlgorithmById(text) if alg is not None: self._addAlgorithm(alg, event.pos()) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText(): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].text(0) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) def _mimeDataAlgorithm(items): item = items[0] mimeData = None if isinstance(item, TreeAlgorithmItem): mimeData = QMimeData() mimeData.setText(item.alg.id()) return mimeData self.algorithmTree.mimeData = _mimeDataAlgorithm self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.textChanged) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.fillInputsTree() self.fillTreeUsingProviders() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", "Model doesn't contain any algorithm and/or parameter and can't be executed", level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as PDF", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", "Model was correctly exported as SVG", level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Python files (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", "Model was correctly exported as python script", level=Qgis.Success, duration=5) def saveModel(self, saveAs): if str(self.textGroup.text()).strip() == '' \ or str(self.textName.text()).strip() == '': QMessageBox.warning( self, self.tr('Warning'), self.tr('Please enter group and model names before saving') ) return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), QCoreApplication.translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option.")) ) return self.update_model.emit() self.bar.pushMessage("", "Model was correctly saved", level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical(self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() paramType = str(item.text(0)) self.addInputOfType(paramType) def addInputOfType(self, paramType, pos=None): if paramType in ModelerParameterDefinitionDialog.paramTypes: dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def textChanged(self): text = self.searchBox.text().strip(' ').lower() for item in list(self.disabledProviderItems.values()): item.setHidden(True) self._filterItem(self.algorithmTree.invisibleRootItem(), [t for t in text.split(' ') if t]) if text: self.algorithmTree.expandAll() self.disabledWithMatchingAlgs = [] for provider in QgsApplication.processingRegistry().providers(): if not provider.isActive(): for alg in provider.algorithms(): if text in alg.name(): self.disabledWithMatchingAlgs.append(provider.id()) break else: self.algorithmTree.collapseAll() def _filterItem(self, item, text): if (item.childCount() > 0): show = False for i in range(item.childCount()): child = item.child(i) showChild = self._filterItem(child, text) show = (showChild or show) and item not in list(self.disabledProviderItems.values()) item.setHidden(not show) return show elif isinstance(item, (TreeAlgorithmItem, TreeActionItem)): # hide if every part of text is not contained somewhere in either the item text or item user role item_text = [item.text(0).lower(), item.data(0, ModelerDialog.NAME_ROLE).lower()] if isinstance(item, TreeAlgorithmItem): item_text.append(item.alg.id()) item_text.extend(item.data(0, ModelerDialog.TAG_ROLE)) hide = bool(text) and not all( any(part in t for t in item_text) for part in text) item.setHidden(hide) return not hide else: item.setHidden(True) return False def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) for paramType in sorted(ModelerParameterDefinitionDialog.paramTypes): paramItem = QTreeWidgetItem() paramItem.setText(0, paramType) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, ModelerParameterDefinitionDialog.inputTooltip(paramType)) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): item = self.algorithmTree.currentItem() if isinstance(item, TreeAlgorithmItem): alg = QgsApplication.processingRegistry().createAlgorithmById(item.alg.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = None try: dlg = alg.getCustomModelerParametersDialog(self.model) except: pass if not dlg: dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def fillTreeUsingProviders(self): self.algorithmTree.clear() self.disabledProviderItems = {} # TODO - replace with proper model for toolbox! # first add qgis/native providers, since they create top level groups for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): self.addAlgorithmsFromProvider(provider, self.algorithmTree.invisibleRootItem()) else: continue self.algorithmTree.sortItems(0, Qt.AscendingOrder) for provider in QgsApplication.processingRegistry().providers(): if provider.id() in ('qgis', 'native', '3d'): # already added continue else: providerItem = TreeProviderItem(provider, self.algorithmTree, self) if not provider.isActive(): providerItem.setHidden(True) self.disabledProviderItems[provider.id()] = providerItem # insert non-native providers at end of tree, alphabetically for i in range(self.algorithmTree.invisibleRootItem().childCount()): child = self.algorithmTree.invisibleRootItem().child(i) if isinstance(child, TreeProviderItem): if child.text(0) > providerItem.text(0): break self.algorithmTree.insertTopLevelItem(i + 1, providerItem) def addAlgorithmsFromProvider(self, provider, parent): groups = {} count = 0 algs = provider.algorithms() active = provider.isActive() # Add algorithms for alg in algs: if alg.flags() & QgsProcessingAlgorithm.FlagHideFromModeler: continue groupItem = None if alg.group() in groups: groupItem = groups[alg.group()] else: # check if group already exists for i in range(parent.childCount()): if parent.child(i).text(0) == alg.group(): groupItem = parent.child(i) groups[alg.group()] = groupItem break if not groupItem: groupItem = TreeGroupItem(alg.group()) if not active: groupItem.setInactive() if provider.id() in ('qgis', 'native', '3d'): groupItem.setIcon(0, provider.icon()) groups[alg.group()] = groupItem algItem = TreeAlgorithmItem(alg) if not active: algItem.setForeground(0, Qt.darkGray) groupItem.addChild(algItem) count += 1 text = provider.name() if not provider.id() in ('qgis', 'native', '3d'): if not active: def activateProvider(): self.activateProvider(provider.id()) label = QLabel(text + " <a href='%s'>Activate</a>") label.setStyleSheet("QLabel {background-color: white; color: grey;}") label.linkActivated.connect(activateProvider) self.algorithmTree.setItemWidget(parent, 0, label) else: parent.setText(0, text) for group, groupItem in sorted(groups.items(), key=operator.itemgetter(1)): parent.addChild(groupItem) if not provider.id() in ('qgis', 'native', '3d'): parent.setHidden(parent.childCount() == 0)
class ShellOutputScintilla(QsciScintilla): DEFAULT_COLOR = "#4d4d4c" KEYWORD_COLOR = "#8959a8" CLASS_COLOR = "#4271ae" METHOD_COLOR = "#4271ae" DECORATION_COLOR = "#3e999f" NUMBER_COLOR = "#c82829" COMMENT_COLOR = "#8e908c" COMMENT_BLOCK_COLOR = "#8e908c" BACKGROUND_COLOR = "#ffffff" CURSOR_COLOR = "#636363" CARET_LINE_COLOR = "#efefef" SINGLE_QUOTE_COLOR = "#718c00" DOUBLE_QUOTE_COLOR = "#718c00" TRIPLE_SINGLE_QUOTE_COLOR = "#eab700" TRIPLE_DOUBLE_QUOTE_COLOR = "#eab700" MARGIN_BACKGROUND_COLOR = "#efefef" MARGIN_FOREGROUND_COLOR = "#636363" SELECTION_BACKGROUND_COLOR = "#d7d7d7" SELECTION_FOREGROUND_COLOR = "#303030" MATCHED_BRACE_BACKGROUND_COLOR = "#b7f907" MATCHED_BRACE_FOREGROUND_COLOR = "#303030" def __init__(self, parent=None): super(ShellOutputScintilla, self).__init__(parent) self.parent = parent self.shell = self.parent.shell self.settings = QgsSettings() # Creates layout for message bar self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # messageBar instance self.infoBar = QgsMessageBar() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # Enable non-ascii chars for editor self.setUtf8(True) sys.stdout = writeOut(self, sys.stdout) sys.stderr = writeOut(self, sys.stderr, "_traceback") self.insertInitText() self.refreshSettingsOutput() self.setReadOnly(True) # Set the default font font = QFontDatabase.systemFont(QFontDatabase.FixedFont) self.setFont(font) self.setMarginsFont(font) # Margin 0 is used for line numbers self.setMarginWidth(0, 0) self.setMarginWidth(1, 0) self.setMarginWidth(2, 0) # fm = QFontMetrics(font) self.setMarginsFont(font) self.setMarginWidth(1, "00000") self.setMarginLineNumbers(1, True) self.setCaretLineVisible(True) self.setCaretWidth(0) self.setMinimumHeight(120) self.setWrapMode(QsciScintilla.WrapCharacter) self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0) self.runScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_E), self) self.runScut.setContext(Qt.WidgetShortcut) self.runScut.activated.connect(self.enteredSelected) # Reimplemented copy action to prevent paste prompt (>>>,...) in command view self.copyShortcut = QShortcut(QKeySequence.Copy, self) self.copyShortcut.activated.connect(self.copy) self.selectAllShortcut = QShortcut(QKeySequence.SelectAll, self) self.selectAllShortcut.activated.connect(self.selectAll) def insertInitText(self): txtInit = QCoreApplication.translate("PythonConsole", "Python Console\n" "Use iface to access QGIS API interface or Type help(iface) for more info\n" "Security warning: typing commands from an untrusted source can lead to data loss and/or leak") # some translation string for the console header ends without '\n' # and the first command in console will be appended at the header text. # The following code add a '\n' at the end of the string if not present. if txtInit.endswith('\n'): self.setText(txtInit) else: self.setText(txtInit + '\n') def refreshSettingsOutput(self): # Set Python lexer self.setLexers() self.setSelectionForegroundColor(QColor( self.settings.value("pythonConsole/selectionForegroundColor", QColor(self.SELECTION_FOREGROUND_COLOR)))) self.setSelectionBackgroundColor(QColor( self.settings.value("pythonConsole/selectionBackgroundColor", QColor(self.SELECTION_BACKGROUND_COLOR)))) self.setMarginsForegroundColor( QColor(self.settings.value("pythonConsole/marginForegroundColor", QColor(self.MARGIN_FOREGROUND_COLOR)))) self.setMarginsBackgroundColor( QColor(self.settings.value("pythonConsole/marginBackgroundColor", QColor(self.MARGIN_BACKGROUND_COLOR)))) caretLineColor = self.settings.value("pythonConsole/caretLineColor", QColor(self.CARET_LINE_COLOR)) cursorColor = self.settings.value("pythonConsole/cursorColor", QColor(self.CURSOR_COLOR)) self.setCaretLineBackgroundColor(caretLineColor) self.setCaretForegroundColor(cursorColor) def setLexers(self): self.lexer = QsciLexerPython() font = QFontDatabase.systemFont(QFontDatabase.FixedFont) loadFont = self.settings.value("pythonConsole/fontfamilytext") if loadFont: font.setFamily(loadFont) fontSize = self.settings.value("pythonConsole/fontsize", type=int) if fontSize: font.setPointSize(fontSize) self.lexer.setDefaultFont(font) self.lexer.setDefaultColor( QColor(self.settings.value("pythonConsole/defaultFontColor", QColor(self.DEFAULT_COLOR)))) self.lexer.setColor(QColor(self.settings.value("pythonConsole/commentFontColor", QColor(self.COMMENT_COLOR))), 1) self.lexer.setColor(QColor(self.settings.value("pythonConsole/numberFontColor", QColor(self.NUMBER_COLOR))), 2) self.lexer.setColor(QColor(self.settings.value("pythonConsole/keywordFontColor", QColor(self.KEYWORD_COLOR))), 5) self.lexer.setColor(QColor(self.settings.value("pythonConsole/classFontColor", QColor(self.CLASS_COLOR))), 8) self.lexer.setColor(QColor(self.settings.value("pythonConsole/methodFontColor", QColor(self.METHOD_COLOR))), 9) self.lexer.setColor(QColor(self.settings.value("pythonConsole/decorFontColor", QColor(self.DECORATION_COLOR))), 15) self.lexer.setColor( QColor(self.settings.value("pythonConsole/commentBlockFontColor", QColor(self.COMMENT_BLOCK_COLOR))), 12) self.lexer.setColor( QColor(self.settings.value("pythonConsole/singleQuoteFontColor", QColor(self.SINGLE_QUOTE_COLOR))), 4) self.lexer.setColor( QColor(self.settings.value("pythonConsole/doubleQuoteFontColor", QColor(self.DOUBLE_QUOTE_COLOR))), 3) self.lexer.setColor(QColor( self.settings.value("pythonConsole/tripleSingleQuoteFontColor", QColor(self.TRIPLE_SINGLE_QUOTE_COLOR))), 6) self.lexer.setColor(QColor( self.settings.value("pythonConsole/tripleDoubleQuoteFontColor", QColor(self.TRIPLE_DOUBLE_QUOTE_COLOR))), 7) self.lexer.setColor( QColor(self.settings.value("pythonConsole/defaultFontColorEditor", QColor(self.DEFAULT_COLOR))), 13) self.lexer.setColor(QColor(Qt.red), 14) self.lexer.setFont(font, 1) self.lexer.setFont(font, 2) self.lexer.setFont(font, 3) self.lexer.setFont(font, 4) self.lexer.setFont(font, QsciLexerPython.UnclosedString) for style in range(0, 33): paperColor = QColor( self.settings.value("pythonConsole/paperBackgroundColor", QColor(self.BACKGROUND_COLOR))) self.lexer.setPaper(paperColor, style) self.setLexer(self.lexer) def clearConsole(self): self.setText('') self.insertInitText() self.shell.setFocus() def contextMenuEvent(self, e): menu = QMenu(self) iconRun = QgsApplication.getThemeIcon("console/mIconRunConsole.svg") iconClear = QgsApplication.getThemeIcon("console/iconClearConsole.svg") iconHideTool = QgsApplication.getThemeIcon("console/iconHideToolConsole.svg") iconSettings = QgsApplication.getThemeIcon("console/iconSettingsConsole.svg") menu.addAction(iconHideTool, QCoreApplication.translate("PythonConsole", "Hide/Show Toolbar"), self.hideToolBar) menu.addSeparator() showEditorAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Show Editor"), self.showEditor) menu.addSeparator() runAction = menu.addAction(iconRun, QCoreApplication.translate("PythonConsole", "Enter Selected"), self.enteredSelected, QKeySequence(Qt.CTRL + Qt.Key_E)) clearAction = menu.addAction(iconClear, QCoreApplication.translate("PythonConsole", "Clear Console"), self.clearConsole) menu.addSeparator() copyAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Copy"), self.copy, QKeySequence.Copy) selectAllAction = menu.addAction( QCoreApplication.translate("PythonConsole", "Select All"), self.selectAll, QKeySequence.SelectAll) menu.addSeparator() menu.addAction(iconSettings, QCoreApplication.translate("PythonConsole", "Options…"), self.parent.openSettings) runAction.setEnabled(False) clearAction.setEnabled(False) copyAction.setEnabled(False) selectAllAction.setEnabled(False) showEditorAction.setEnabled(True) if self.hasSelectedText(): runAction.setEnabled(True) copyAction.setEnabled(True) if not self.text(3) == '': selectAllAction.setEnabled(True) clearAction.setEnabled(True) if self.parent.tabEditorWidget.isVisible(): showEditorAction.setEnabled(False) menu.exec_(self.mapToGlobal(e.pos())) def hideToolBar(self): tB = self.parent.toolBar tB.hide() if tB.isVisible() else tB.show() self.shell.setFocus() def showEditor(self): Ed = self.parent.splitterObj if not Ed.isVisible(): Ed.show() self.parent.showEditorButton.setChecked(True) self.shell.setFocus() def copy(self): """Copy text to clipboard... or keyboard interrupt""" if self.hasSelectedText(): text = self.selectedText() text = text.replace('>>> ', '').replace('... ', '').strip() # removing prompts QApplication.clipboard().setText(text) else: raise KeyboardInterrupt def enteredSelected(self): cmd = self.selectedText() self.shell.insertFromDropPaste(cmd) self.shell.entered() def keyPressEvent(self, e): # empty text indicates possible shortcut key sequence so stay in output txt = e.text() if len(txt) and txt >= " ": self.shell.append(txt) self.shell.move_cursor_to_end() self.shell.setFocus() e.ignore() else: # possible shortcut key sequence, accept it e.accept() def widgetMessageBar(self, iface, text): timeout = iface.messageTimeout() self.infoBar.pushMessage(text, Qgis.Info, timeout)
class BackupedImgUploaderWizard(QtGui.QWizard, FORM_CLASS): def __init__(self, iface, settings, parent=None): """Constructor.""" super(BackupedImgUploaderWizard, 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.iface = iface self.setupUi(self) # Message bars need to be attached to pages, since the wizard object # does not have a layout. It doesn't work to attach the same bar # object to all pages (it is only shown in the last one). The only way # I could make it work was to create different QgsMessageBar objects, # one per page, but it is very to keep track of those references # along the code. It is messy, there should be a better solution. self.bar0 = QgsMessageBar() self.bar0.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(0).layout().addWidget(self.bar0) self.bar1 = QgsMessageBar() self.bar1.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(1).layout().addWidget(self.bar1) self.bar2 = QgsMessageBar() self.bar2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.page(2).layout().addWidget(self.bar2) self.setButtonText(QtGui.QWizard.CustomButton1, self.tr("&Start upload")); self.setOption(QtGui.QWizard.HaveCustomButton1, True); self.settings = settings # Dictionaries to save imagery info (todo: defined as a classes in the future) self.metadata = {} self.reprojected = [] self.licensed = [] # Initialize layers and default settings self.loadLayers() self.loadMetadataSettings() self.loadStorageSettings() self.loadOptionsSettings() # register event handlers """ List of page navigation buttons in QWizard, for reference. Please comment out and implement following functions if necessary.""" #self.button(QWizard.BackButton).clicked.connect(self.previousPage) #self.button(QWizard.NextButton).clicked.connect(self.nextPage) #self.button(QWizard.FinishButton).clicked.connect(self.finishWizard) #self.button(QWizard.CancelButton).clicked.connect(self.cancelWizard) # Imagery connections (wizard page 1) self.layers_tool_button.clicked.connect(self.loadLayers) self.file_tool_button.clicked.connect(self.selectFile) self.add_source_button.clicked.connect(self.addSources) self.remove_source_button.clicked.connect(self.removeSources) self.up_source_button.clicked.connect(self.upSource) self.down_source_button.clicked.connect(self.downSource) # Metadata connections (wizard page 2) self.sense_start_edit.setCalendarPopup(1) self.sense_start_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.sense_end_edit.setCalendarPopup(1) self.sense_end_edit.setDisplayFormat('dd.MM.yyyy HH:mm') self.default_button.clicked.connect(self.loadMetadataSettings) self.clean_button.clicked.connect(self.cleanMetadataSettings) self.save_button.clicked.connect(self.saveMetadata) # Upload tab connections (wizard page 3) self.storage_combo_box.currentIndexChanged.connect(self.enableSpecify) self.customButtonClicked.connect(self.startUploader) # event handling for wizard page 1 def loadLayers(self): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if not self.layers_list_widget.findItems(layer.name(),Qt.MatchExactly): item = QListWidgetItem() item.setText(layer.name()) item.setData(Qt.UserRole, layer.dataProvider().dataSourceUri()) self.layers_list_widget.addItem(item) def selectFile(self): selected_file = QFileDialog.getOpenFileName( self, 'Select imagery file', os.path.expanduser("~")) self.source_file_edit.setText(selected_file) def addSources(self): filename = self.source_file_edit.text() selected_layers = self.layers_list_widget.selectedItems() if not filename and not selected_layers: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'Either a layer or file should be selected to be added', level=QgsMessageBar.WARNING) else: added = False if filename: if self.validateFile(filename): if not self.sources_list_widget.findItems(filename,Qt.MatchExactly): item = QListWidgetItem() item.setText(os.path.basename(filename)) item.setData(Qt.UserRole, filename) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) self.source_file_edit.setText('') added = True if selected_layers: for item in selected_layers: if self.validateLayer(item.text()): if not self.sources_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.takeItem(self.layers_list_widget.row(item)) self.sources_list_widget.addItem(item) self.added_sources_list_widget.addItem(item.clone()) added = True if added: self.bar0.clearWidgets() self.bar0.pushMessage( 'INFO', 'Source(s) added to the upload queue', level=QgsMessageBar.INFO) self.loadMetadataReviewBox() def removeSources(self): selected_sources = self.sources_list_widget.selectedItems() added_sources = self.added_sources_list_widget.selectedItems() if selected_sources: for item in selected_sources: self.sources_list_widget.takeItem(self.sources_list_widget.row(item)) added_item = self.added_sources_list_widget.findItems(item.text(),Qt.MatchExactly) if added_item: self.added_sources_list_widget.takeItem(self.added_sources_list_widget.row(added_item[0])) all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if item.text() == layer.name(): if not self.layers_list_widget.findItems(item.text(),Qt.MatchExactly): self.layers_list_widget.addItem(item) else: self.bar0.clearWidgets() self.bar0.pushMessage( 'WARNING', 'An imagery source must be selected to be removed', level=QgsMessageBar.WARNING) def upSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position > 0: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position-1,item) item.setSelected(1) def downSource(self): selected_layers = self.sources_list_widget.selectedItems() if selected_layers: position = self.sources_list_widget.row(selected_layers[0]) if position < self.sources_list_widget.count()-1: item = self.sources_list_widget.takeItem(position) self.sources_list_widget.insertItem(position+1,item) item.setSelected(1) # event handling for wizard page 2 def loadMetadataSettings(self): self.settings.beginGroup("Metadata") self.title_edit.setText(self.settings.value('TITLE')) if self.settings.value('PLATFORM') == None: self.platform_combo_box.setCurrentIndex(0) else: self.platform_combo_box.setCurrentIndex(int(self.settings.value('PLATFORM'))) self.sensor_edit.setText(self.settings.value('SENSOR')) self.sensor_edit.setCursorPosition(0) """ self.sense_start_edit.setDateTime(QDateTime(self.settings.value('SENSE_START'))) self.sense_end_edit.setDateTime(QDateTime(self.settings.value('SENSE_END'))) """ self.sense_start_edit.setDate(QDateTime.fromString( self.settings.value('SENSE_START'), Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_START'), Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime.fromString(self.settings.value('SENSE_END'), Qt.ISODate).time()) self.tags_edit.setText(self.settings.value('TAGS')) self.tags_edit.setCursorPosition(0) self.provider_edit.setText(self.settings.value('PROVIDER')) self.provider_edit.setCursorPosition(0) self.contact_edit.setText(self.settings.value('CONTACT')) self.contact_edit.setCursorPosition(0) self.website_edit.setText(self.settings.value('WEBSITE')) self.website_edit.setCursorPosition(0) """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('LICENSE')).lower() == 'true': self.license_check_box.setCheckState(2) if str(self.settings.value('REPROJECT')).lower() == 'true': self.reproject_check_box.setCheckState(2) self.settings.endGroup() def cleanMetadataSettings(self): self.title_edit.setText('') self.platform_combo_box.setCurrentIndex(0) self.sensor_edit.setText('') self.sense_start_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).date()) self.sense_start_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00', Qt.ISODate).time()) self.sense_end_edit.setDate( QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).date()) self.sense_end_edit.setTime( QDateTime().fromString('1970-01-01T00:00:00',Qt.ISODate).time()) self.tags_edit.setText('') self.provider_edit.setText('') self.contact_edit.setText('') self.website_edit.setText('') self.license_check_box.setCheckState(0) self.reproject_check_box.setCheckState(0) def saveMetadata(self): selected_layers = self.added_sources_list_widget.selectedItems() if selected_layers: for item in selected_layers: filename = item.data(Qt.UserRole) self.loadImageryInfo(filename) json_string = json.dumps(self.metadata[filename],indent=4,separators=(',', ': ')) if filename not in self.reprojected: json_filename = os.path.splitext(filename)[0]+'.json' else: # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: json_filename = os.path.splitext(filename)[0]+'_EPSG3857.json' json_file = open(json_filename, 'w') print >> json_file, json_string json_file.close() self.loadMetadataReviewBox() self.bar1.clearWidgets() self.bar1.pushMessage( 'INFO', 'Metadata for the selected sources were saved', level=QgsMessageBar.INFO) else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'One or more source imagery should be selected to have the metadata saved', level=QgsMessageBar.WARNING) # event handling for wizard page 3 # also see multi-thread for startUploader function def enableSpecify(self): if self.storage_combo_box.currentIndex() == 1: self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) else: self.specify_label.setEnabled(0) self.specify_edit.setText('') self.specify_edit.setEnabled(0) def loadStorageSettings(self): self.settings.beginGroup("Storage") bucket = self.settings.value('S3_BUCKET_NAME') storage_index = self.storage_combo_box.findText(bucket,Qt.MatchExactly) if not storage_index == -1: self.storage_combo_box.setCurrentIndex(storage_index) else: self.storage_combo_box.setCurrentIndex(self.storage_combo_box.findText(self.tr('other...'))) self.specify_label.setEnabled(1) self.specify_edit.setEnabled(1) self.specify_edit.setText(self.settings.value('S3_BUCKET_NAME')) self.key_id_edit.setText(self.settings.value('AWS_ACCESS_KEY_ID')) self.key_id_edit.setCursorPosition(0) self.secret_key_edit.setText(self.settings.value('AWS_SECRET_ACCESS_KEY')) self.secret_key_edit.setCursorPosition(0) self.settings.endGroup() def loadOptionsSettings(self): self.settings.beginGroup("Options") """ Boolean values are converted into string and lower case for 'if' statement, since PyQt sometimes returns 'true', just like C++, instead of 'True', Python style. Maybe we can use integer values (0 or 1), instead of using string. """ if str(self.settings.value('NOTIFY_OAM')).lower() == 'true': self.notify_oam_check.setCheckState(2) if str(self.settings.value('TRIGGER_OAM_TS')).lower() == 'true': self.trigger_tiling_check.setCheckState(2) self.settings.endGroup() # other functions def validateFile(self,filename): # check that file exists if not os.path.exists(filename): self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s does not exist" % filename, level=QgsMessageBar.CRITICAL) return False # check that file is an image if imghdr.what(filename) is None: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s is not a supported data source" % filename, level=QgsMessageBar.CRITICAL) return False # check if gdal can read file try: raster = gdal.Open(filename,gdal.GA_ReadOnly) except: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check if there is an object raster if not raster: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "GDAL could not read file %s" % filename, level=QgsMessageBar.CRITICAL) return False # check that image has at least 3 bands if raster.RasterCount < 3: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "The file %s has less than 3 raster bands" % filename, level=QgsMessageBar.CRITICAL) return False # check if projection is set if raster.GetProjection() is '': self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Could not extract projection from file %s" % filename, level=QgsMessageBar.CRITICAL) return False # finally, check if bbox has valid data xy_points = [(0.0,0.0),(0.0,raster.RasterYSize),(raster.RasterXSize,0.0),(raster.RasterXSize,raster.RasterYSize)] for point in xy_points: if point != self.GDALInfoReportCorner(raster,point[0],point[1]): QgsMessageLog.logMessage( 'File %s is a valid data source' % filename, 'OAM', level=QgsMessageLog.INFO) return True def validateLayer(self,layer_name): all_layers = self.iface.mapCanvas().layers() for layer in all_layers: if layer_name == layer.name(): if layer.type() == QgsMapLayer.VectorLayer: self.bar0.clearWidgets() self.bar0.pushMessage( "CRITICAL", "Vector layers cannot be selected for upload", level=QgsMessageBar.CRITICAL) return 0 else: return 1 def extractMetadata(self,filename): """Extract filesize, projection, bbox and gsd from image file""" self.metadata[filename]['File size'] = os.stat(filename).st_size datafile = gdal.Open(filename,gdal.GA_ReadOnly) if datafile is None: self.bar1.clearWidgets() self.bar1.pushMessage( 'CRITICAL', 'Extraction of raster metadata failed', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage( 'Failed to extract metadata', 'OAM', level=QgsMessageLog.CRITICAL) else: # extract projection projInfo = datafile.GetProjection() # WKT format spatialRef = osr.SpatialReference() spatialRef.ImportFromWkt(projInfo) # Proj4 format spatialRefProj = spatialRef.ExportToProj4() self.metadata[filename]['Projection'] = str(spatialRefProj) # original bbox upper_left = self.GDALInfoReportCorner(datafile,0.0,0.0 ); lower_left = self.GDALInfoReportCorner(datafile,0.0,datafile.RasterYSize); upper_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,0.0 ); lower_right = self.GDALInfoReportCorner(datafile,datafile.RasterXSize,datafile.RasterYSize ); center = self.GDALInfoReportCorner(datafile,datafile.RasterXSize/2.0,datafile.RasterYSize/2.0 ); # get new bbox values if reprojection will happen try: if filename in self.reprojected: self.metadata[filename]['Projection'] = "EPSG:3857" target = osr.SpatialReference() target.ImportFromEPSG(3857) transform = osr.CoordinateTransformation(spatialRef,target) point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_left[0],upper_left[1])) point.Transform(transform) upper_left = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_left[0],lower_left[1])) point.Transform(transform) lower_left = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (upper_right[0],upper_right[1])) point.Transform(transform) upper_right = json.loads(point.ExportToJson())['coordinates'] point = ogr.CreateGeometryFromWkt("POINT (%f %f)" % (lower_right[0],lower_right[1])) point.Transform(transform) lower_right = json.loads(point.ExportToJson())['coordinates'] except (RuntimeError, TypeError, NameError) as error: print error except: print "Unexpected error:", sys.exc_info()[0] self.metadata[filename]['BBOX'] = (upper_left,lower_left,upper_right,lower_right) def GDALInfoReportCorner(self,hDataset,x,y): """GDALInfoReportCorner: extracted and adapted from the python port of gdalinfo""" # Transform the point into georeferenced coordinates adfGeoTransform = hDataset.GetGeoTransform(can_return_null = True) if adfGeoTransform is not None: dfGeoX = adfGeoTransform[0] + adfGeoTransform[1] * x + adfGeoTransform[2] * y dfGeoY = adfGeoTransform[3] + adfGeoTransform[4] * x + adfGeoTransform[5] * y else: self.bar1.clearWidgets() self.bar1.pushMessage( 'WARNING', 'BBOX might be wrong. Transformation coefficient could not be fetched from raster', level=QgsMessageBar.WARNING) return (x,y) # Report the georeferenced coordinates if abs(dfGeoX) < 181 and abs(dfGeoY) < 91: return literal_eval(("(%12.7f,%12.7f) " % (dfGeoX, dfGeoY ))) else: return literal_eval(("(%12.3f,%12.3f) " % (dfGeoX, dfGeoY ))) def loadImageryInfoForm(self, filename): pass def loadImageryInfo(self, filename): self.metadata[filename] = {} self.metadata[filename]['Title'] = self.title_edit.text() self.metadata[filename]['Platform'] = self.platform_combo_box.currentIndex() self.metadata[filename]['Sensor'] = self.sensor_edit.text() start = QDateTime() start.setDate(self.sense_start_edit.date()) start.setTime(self.sense_start_edit.time()) self.metadata[filename]['Sensor start'] = start.toString(Qt.ISODate) end = QDateTime() end.setDate(self.sense_end_edit.date()) end.setTime(self.sense_end_edit.time()) self.metadata[filename]['Sensor end'] = end.toString(Qt.ISODate) self.metadata[filename]['Tags'] = self.tags_edit.text() self.metadata[filename]['Provider'] = self.provider_edit.text() self.metadata[filename]['Contact'] = self.contact_edit.text() self.metadata[filename]['Website'] = self.website_edit.text() if self.reproject_check_box.isChecked(): if filename not in self.reprojected: self.reprojected.append(filename) else: while filename in self.reprojected: self.reprojected.remove(filename) if self.license_check_box.isChecked(): self.metadata[filename]['License'] = "Licensed under CC-BY 4.0 and allow tracing in OSM" if filename not in self.licensed: self.licensed.append(filename) else: while filename in self.licensed: self.licensed.remove(filename) self.extractMetadata(filename) def loadMetadataReviewBox(self): json_filenames = [] for index in xrange(self.sources_list_widget.count()): filename = str(self.sources_list_widget.item(index).data(Qt.UserRole)) if filename not in self.reprojected: f = os.path.splitext(filename)[0]+'.json' else: f = os.path.splitext(filename)[0]+'_EPSG3857.json' json_filenames.append(f) temp = QTemporaryFile() temp.open() for f in json_filenames: if os.path.exists(f): with open(f) as infile: temp.write(infile.read()) temp.flush() temp.seek(0) stream = QTextStream(temp) self.review_metadata_box.setText(stream.readAll()) #functions for threading purpose def startConnection(self): if self.storage_combo_box.currentIndex() == 0: bucket_name = 'oam-qgis-plugin-test' else: bucket_name = str(self.specify_edit.text()) if not bucket_name: self.bar2.clearWidgets() self.bar2.pushMessage( 'WARNING', 'The bucket for upload must be provided', level=QgsMessageBar.WARNING) bucket_key = str(self.key_id_edit.text()) bucket_secret = str(self.secret_key_edit.text()) self.bucket = None for trial in xrange(3): if self.bucket: break try: connection = S3Connection(bucket_key,bucket_secret) self.bucket = connection.get_bucket(bucket_name) QgsMessageLog.logMessage( 'Connection established' % trial, 'OAM', level=QgsMessageLog.INFO) except: if trial == 2: QgsMessageLog.logMessage( 'Failed to connect after 3 attempts', 'OAM', level=QgsMessageLog.CRITICAL) return self.bucket def reproject(self,filename): # to avoid repetition of "EPSG3857" in filename: if not "EPSG3857" in filename: reproject_filename = os.path.splitext(filename)[0]+'_EPSG3857.tif' os.system("gdalwarp -of GTiff -t_srs epsg:3857 %s %s" % (filename,reproject_filename)) QgsMessageLog.logMessage( 'Reprojected to EPSG:3857', 'OAM', level=QgsMessageLog.INFO) return reproject_filename def convert(self,filename): tif_filename = os.path.splitext(filename)[0]+".tif" #Open existing dataset src_ds = gdal.Open(filename) driver = gdal.GetDriverByName("GTiff") dst_ds = driver.CreateCopy(tif_filename, src_ds, 0 ) #Properly close the datasets to flush to disk dst_ds = None src_ds = None def startUploader(self): # initialize options self.upload_options = [] if self.notify_oam_check.isChecked(): self.upload_options.append("notify_oam") if self.trigger_tiling_check.isChecked(): self.upload_options.append("trigger_tiling") if self.startConnection(): for index in xrange(self.sources_list_widget.count()): filename = str(self.sources_list_widget.item(index).data(Qt.UserRole)) self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Pre-upload image processing...', level=QgsMessageBar.INFO) # Perfom reprojection if filename in self.reprojected: filename = self.reproject(filename) QgsMessageLog.logMessage( 'Created reprojected file: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # Convert file format if not (imghdr.what(filename) == 'tiff'): filename = self.convert(filename) QgsMessageLog.logMessage( 'Converted file to tiff: %s' % filename, 'OAM', level=QgsMessageLog.INFO) # create a new uploader instance uploader = Uploader(filename,self.bucket,self.upload_options) QgsMessageLog.logMessage( 'Uploader started\n', 'OAM', level=QgsMessageLog.INFO) # configure the QgsMessageBar messageBar = self.bar2.createMessage('INFO: Performing upload...', ) progressBar = QProgressBar() progressBar.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) messageBar.layout().addWidget(progressBar) cancelButton = QPushButton() cancelButton.setText('Cancel') cancelButton.clicked.connect(self.cancelUpload) messageBar.layout().addWidget(cancelButton) self.bar2.clearWidgets() self.bar2.pushWidget(messageBar, level=QgsMessageBar.INFO) self.messageBar = messageBar # start the worker in a new thread thread = QThread(self) uploader.moveToThread(thread) uploader.finished.connect(self.uploaderFinished) uploader.error.connect(self.uploaderError) uploader.progress.connect(progressBar.setValue) thread.started.connect(uploader.run) thread.start() self.thread = thread self.uploader = uploader else: QgsMessageLog.logMessage( 'No connection to the server\n', 'OAM', level=QgsMessageLog.CRITICAL) def cancelUpload(self): self.uploader.kill() self.bar2.clearWidgets() self.bar2.pushMessage( 'WARNING', 'Canceling upload...', level=QgsMessageBar.WARNING) def uploaderFinished(self, success): # clean up the uploader and thread try: self.uploader.deleteLater() except: QgsMessageLog.logMessage( 'Exception on deleting uploader\n', 'OAM', level=QgsMessageLog.CRITICAL) self.thread.quit() self.thread.wait() try: self.thread.deleteLater() except: QgsMessageLog.logMessage( 'Exception on deleting thread\n', 'OAM', level=QgsMessageLog.CRITICAL) # remove widget from message bar self.bar2.popWidget(self.messageBar) if success: # report the result self.bar2.clearWidgets() self.bar2.pushMessage( 'INFO', 'Upload completed with success', level=QgsMessageBar.INFO) QgsMessageLog.logMessage( 'Upload succeeded', 'OAM', level=QgsMessageLog.INFO) else: # notify the user that something went wrong self.bar2.pushMessage( 'CRITICAL', 'Upload was interrupted', level=QgsMessageBar.CRITICAL) QgsMessageLog.logMessage( 'Upload was interrupted', 'OAM', level=QgsMessageLog.CRITICAL) def uploaderError(self, e, exception_string): QgsMessageLog.logMessage( 'Uploader thread raised an exception:\n'.format(exception_string), 'OAM', level=QgsMessageLog.CRITICAL)
class ModelerParametersDialog(QDialog): def __init__(self, alg, model, algName=None, configuration=None): QDialog.__init__(self) self.setModal(True) self._alg = alg # The algorithm to define in this dialog. It is an instance of QgsProcessingAlgorithm self.model = model # The model this algorithm is going to be added to. It is an instance of QgsProcessingModelAlgorithm self.childId = algName # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.configuration = configuration self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry( settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.algorithmItem = None self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) self.algorithmItem = QgsGui.instance().processingGuiRegistry( ).algorithmConfigurationWidget(self._alg) if self.configuration: self.algorithmItem.setConfiguration(self.configuration) self.verticalLayout.addWidget(self.algorithmItem) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText( self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination( ) or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget tooltip = param.description() widget.setToolTip(tooltip) if param.flags( ) & QgsProcessingParameterDefinition.FlagAdvanced: wrapper.label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.verticalLayout.addWidget(wrapper.label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterVectorDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText( self.tr('[Enter name if this is a final result]')) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([ alg.description() for alg in self.getAvailableDependencies() ]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.wrappers[param.name()].widget.setVisible( self.showAdvanced) self.wrappers[param.name()].label.setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, (tuple, list)): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, (tuple, list)): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [ p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition) ], [ o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition) ], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source( ) == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source( ) == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition( value.parameterName()).description() elif value.source( ) == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition( value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags( ) & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None if value is None: value = param.defaultValue() if isinstance( value, QgsProcessingModelChildParameterSource ) and value.source( ) == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() self.wrappers[param.name()].setValue(value) for name, out in list(alg.modelOutputs().items()): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) if self.algorithmItem: alg.setConfiguration(self.algorithmItem.configuration()) self._alg = alg.algorithm().create( self.algorithmItem.configuration()) for param in self._alg.parameterDefinitions(): if param.isDestination( ) or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue try: val = self.wrappers[param.name()].value() except InvalidParameterValue: self.bar.pushMessage( self.tr("Error"), self.tr( "Wrong or missing value for parameter '{}'").format( param.description()), level=Qgis.Warning) return None if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] elif not (isinstance(val, list) and all([ isinstance(subval, QgsProcessingModelChildParameterSource) for subval in val ])): val = [ QgsProcessingModelChildParameterSource.fromStaticValue(val) ] for subval in val: if (isinstance(subval, QgsProcessingModelChildParameterSource) and subval.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable(subval.staticValue())) \ or (subval is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage( self.tr("Error"), self.tr("Wrong or missing value for parameter '{}'" ).format(param.description()), level=Qgis.Warning) return None alg.addParameterSources(param.name(), val) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = self.valueItems[dest.name()].text() if name.strip() != '': output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output if dest.flags( ) & QgsProcessingParameterDefinition.FlagIsModelOutput: if dest.name() not in outputs: output = QgsProcessingModelOutput(dest.name(), dest.name()) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[dest.name()] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append( availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) #try: # self._alg.processBeforeAddingToModeler(alg, self.model) #except: # pass return alg def okPressed(self): alg = self.createAlgorithm() if alg is not None: self.accept() def cancelPressed(self): self.reject() def openHelp(self): algHelp = self._alg.helpUrl() if not algHelp: algHelp = QgsHelp.helpUrl("processing_algs/{}/{}.html#{}".format( self._alg.provider().helpId(), self._alg.groupId(), "{}{}".format(self._alg.provider().helpId(), self._alg.name()))).toString() if algHelp not in [None, ""]: webbrowser.open(algHelp)
class ModelerParametersDialog(QDialog): ENTER_NAME = '[Enter name if this is a final result]' NOT_SELECTED = '[Not selected]' USE_MIN_COVERING_EXTENT = '[Use min covering extent]' def __init__(self, alg, model, algName=None): QDialog.__init__(self) self.setModal(True) # The algorithm to define in this dialog. It is an instance of GeoAlgorithm self._alg = alg # The resulting algorithm after the user clicks on OK. it is an instance of the container Algorithm class self.alg = None # The model this algorithm is going to be added to self.model = model # The name of the algorithm in the model, in case we are editing it and not defining it for the first time self.childId = algName self.setupUi() self.params = None settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDialogGeometry", self.saveGeometry()) super(ModelerParametersDialog, self).closeEvent(event) def setupUi(self): self.labels = {} self.widgets = {} self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.resize(650, 450) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Help) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(5) self.verticalLayout.setMargin(20) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setSpacing(5) hLayout.setMargin(0) descriptionLabel = QLabel(self.tr("Description")) self.descriptionBox = QLineEdit() self.descriptionBox.setText(self._alg.displayName()) hLayout.addWidget(descriptionLabel) hLayout.addWidget(self.descriptionBox) self.verticalLayout.addLayout(hLayout) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) self.verticalLayout.addWidget(line) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.advancedButton = QPushButton() self.advancedButton.setText(self.tr('Show advanced parameters')) self.advancedButton.clicked.connect( self.showAdvancedParametersClicked) advancedButtonHLayout = QHBoxLayout() advancedButtonHLayout.addWidget(self.advancedButton) advancedButtonHLayout.addStretch() self.verticalLayout.addLayout(advancedButtonHLayout) break for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr('(xmin, xmax, ymin, ymax)') if isinstance(param, QgsProcessingParameterPoint): desc += self.tr('(x, y)') if param.flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') label = QLabel(desc) self.labels[param.name()] = label wrapper = WidgetWrapperFactory.create_wrapper(param, self) self.wrappers[param.name()] = wrapper widget = wrapper.widget if widget is not None: self.valueItems[param.name()] = widget tooltip = param.description() label.setToolTip(tooltip) widget.setToolTip(tooltip) if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: label.setVisible(self.showAdvanced) widget.setVisible(self.showAdvanced) self.widgets[param.name()] = widget self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) for dest in self._alg.destinationParameterDefinitions(): if dest.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if isinstance(dest, (QgsProcessingParameterRasterDestination, QgsProcessingParameterFeatureSink, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination)): label = QLabel(dest.description()) item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText(ModelerParametersDialog.ENTER_NAME) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(item) self.valueItems[dest.name()] = item label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Parent algorithms')) self.dependenciesPanel = self.getDependenciesPanel() self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependenciesPanel) self.verticalLayout.addStretch(1000) self.setPreviousValues() self.setWindowTitle(self._alg.displayName()) self.verticalLayout2 = QVBoxLayout() self.verticalLayout2.setSpacing(2) self.verticalLayout2.setMargin(0) self.paramPanel = QWidget() self.paramPanel.setLayout(self.verticalLayout) self.scrollArea = QgsScrollArea() self.scrollArea.setWidget(self.paramPanel) self.scrollArea.setWidgetResizable(True) self.verticalLayout2.addWidget(self.scrollArea) self.verticalLayout2.addWidget(self.buttonBox) self.setLayout(self.verticalLayout2) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) QMetaObject.connectSlotsByName(self) for wrapper in list(self.wrappers.values()): wrapper.postInitialize(list(self.wrappers.values())) def getAvailableDependencies(self): # spellok if self.childId is None: dependent = [] else: dependent = list(self.model.dependentChildAlgorithms(self.childId)) dependent.append(self.childId) opts = [] for alg in list(self.model.childAlgorithms().values()): if alg.childId() not in dependent: opts.append(alg) return opts def getDependenciesPanel(self): return MultipleInputPanel([alg.description() for alg in self.getAvailableDependencies()]) # spellok def showAdvancedParametersClicked(self): self.showAdvanced = not self.showAdvanced if self.showAdvanced: self.advancedButton.setText(self.tr('Hide advanced parameters')) else: self.advancedButton.setText(self.tr('Show advanced parameters')) for param in self._alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: self.labels[param.name()].setVisible(self.showAdvanced) self.widgets[param.name()].setVisible(self.showAdvanced) def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): # upgrade paramType to list if paramType is None: paramType = [] elif not isinstance(paramType, list): paramType = [paramType] if outTypes is None: outTypes = [] elif not isinstance(outTypes, list): outTypes = [outTypes] return self.model.availableSourcesForChild(self.childId, [p.typeName() for p in paramType if issubclass(p, QgsProcessingParameterDefinition)], [o.typeName() for o in outTypes if issubclass(o, QgsProcessingOutputDefinition)], dataTypes) def resolveValueDescription(self, value): if isinstance(value, QgsProcessingModelChildParameterSource): if value.source() == QgsProcessingModelChildParameterSource.StaticValue: return value.staticValue() elif value.source() == QgsProcessingModelChildParameterSource.ModelParameter: return self.model.parameterDefinition(value.parameterName()).description() elif value.source() == QgsProcessingModelChildParameterSource.ChildOutput: alg = self.model.childAlgorithm(value.outputChildId()) return self.tr("'{0}' from algorithm '{1}'").format( alg.algorithm().outputDefinition(value.outputName()).description(), alg.description()) return value def setPreviousValues(self): if self.childId is not None: alg = self.model.childAlgorithm(self.childId) self.descriptionBox.setText(alg.description()) for param in alg.algorithm().parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue value = None if param.name() in alg.parameterSources(): value = alg.parameterSources()[param.name()] if isinstance(value, list) and len(value) == 1: value = value[0] elif isinstance(value, list) and len(value) == 0: value = None if value is None: value = param.defaultValue() if isinstance(value, QgsProcessingModelChildParameterSource) and value.source() == QgsProcessingModelChildParameterSource.StaticValue: value = value.staticValue() self.wrappers[param.name()].setValue(value) for name, out in list(alg.modelOutputs().items()): if out.childOutputName() in self.valueItems: self.valueItems[out.childOutputName()].setText(out.name()) selected = [] dependencies = self.getAvailableDependencies() # spellok for idx, dependency in enumerate(dependencies): if dependency.childId() in alg.dependencies(): selected.append(idx) self.dependenciesPanel.setSelectedItems(selected) def createAlgorithm(self): alg = QgsProcessingModelChildAlgorithm(self._alg.id()) if not self.childId: alg.generateChildId(self.model) else: alg.setChildId(self.childId) alg.setDescription(self.descriptionBox.text()) for param in self._alg.parameterDefinitions(): if param.isDestination() or param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue val = self.wrappers[param.name()].value() if (isinstance(val, QgsProcessingModelChildParameterSource) and val.source() == QgsProcessingModelChildParameterSource.StaticValue and not param.checkValueIsAcceptable( val.staticValue())) \ or (val is None and not param.flags() & QgsProcessingParameterDefinition.FlagOptional): self.bar.pushMessage("Error", "Wrong or missing value for parameter '%s'" % param.description(), level=QgsMessageBar.WARNING) return None if val is None: continue elif isinstance(val, QgsProcessingModelChildParameterSource): alg.addParameterSources(param.name(), [val]) elif isinstance(val, list): alg.addParameterSources(param.name(), val) else: alg.addParameterSources(param.name(), [QgsProcessingModelChildParameterSource.fromStaticValue(val)]) outputs = {} for dest in self._alg.destinationParameterDefinitions(): if not dest.flags() & QgsProcessingParameterDefinition.FlagHidden: name = str(self.valueItems[dest.name()].text()) if name.strip() != '' and name != ModelerParametersDialog.ENTER_NAME: output = QgsProcessingModelOutput(name, name) output.setChildId(alg.childId()) output.setChildOutputName(dest.name()) outputs[name] = output alg.setModelOutputs(outputs) selectedOptions = self.dependenciesPanel.selectedoptions availableDependencies = self.getAvailableDependencies() # spellok dep_ids = [] for selected in selectedOptions: dep_ids.append(availableDependencies[selected].childId()) # spellok alg.setDependencies(dep_ids) try: self._alg.processBeforeAddingToModeler(alg, self.model) except: pass return alg def okPressed(self): self.alg = self.createAlgorithm() if self.alg is not None: self.close() def cancelPressed(self): self.alg = None self.close() def openHelp(self): algHelp = self._alg.help() if algHelp is not None: webbrowser.open(algHelp)
class geopunt4QgisPoidialog(QDialog): def __init__(self, iface): QDialog.__init__(self, None) self.setWindowFlags( self.windowFlags() & ~Qt.WindowContextHelpButtonHint ) #self.setWindowFlags( self.windowFlags() |Qt.WindowStaysOnTopHint) self.iface = iface # initialize locale locale = QSettings().value("locale/userLocale", "nl") if not locale: locale == 'nl' 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): 'Set up the user interface from Designer.' self.ui = Ui_geopunt4QgisPoiDlg() self.ui.setupUi(self) #get settings self.s = QSettings() self.loadSettings() #setup geopunt and geometryHelper objects self.gh = geometryHelper(self.iface) self.ph = poiHelper( self.iface) #create the graphicsLayer self.graphicsLayer = [] #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) #table ui self.ui.resultLijst.hideColumn(0) # filters self.firstShow = True self.poiTypes = {} self.poiCategories = {} self.poiThemes = {} #actions self.ui.resultLijst.addAction( self.ui.actionZoomtoSelection ) self.ui.actionZoomtoSelection.triggered.connect( self.onZoomSelClicked) self.ui.resultLijst.addAction( self.ui.actionAddTSeltoMap ) self.ui.actionAddTSeltoMap.triggered.connect( self.onAddSelClicked) #event handlers self.ui.zoekKnop.clicked.connect(self.onZoekActivated) self.ui.zoomSelKnop.clicked.connect(self.onZoomSelClicked) self.ui.resultLijst.itemDoubleClicked.connect(self.onZoomSelClicked ) self.ui.resultLijst.itemSelectionChanged.connect(self.onSelectionChanged) self.ui.addToMapKnop.clicked.connect(self.onAddSelClicked) self.ui.addMinModelBtn.clicked.connect( self.addMinModel ) self.ui.poiText.textChanged.connect( self.searchTxtChanged ) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean ) def loadSettings(self): self.saveToFile = int( self.s.value("geopunt4qgis/poiSavetoFile" , 1)) layerName = self.s.value("geopunt4qgis/poilayerText", "") if layerName: self.layerName= layerName self.timeout = int( self.s.value("geopunt4qgis/timeout" ,15)) if settings().proxyUrl: self.proxy = settings().proxyUrl else: self.proxy = "" self.startDir = self.s.value("geopunt4qgis/startDir", os.path.expanduser("~") ) self.poi = Poi(self.timeout, self.proxy) def show(self): QDialog.show(self) self.setWindowModality(0) if self.firstShow: inet = internet_on(proxyUrl=self.proxy, timeout=self.timeout) #filters if inet: self.poiThemes = dict( self.poi.listPoiThemes() ) poiThemes = [""] + list(self.poiThemes.keys()) poiThemes.sort() self.ui.filterPoiThemeCombo.addItems( poiThemes ) self.poiCategories = dict( self.poi.listPoiCategories() ) poiCategories = [""] + list(self.poiCategories.keys()) poiCategories.sort() self.ui.filterPoiCategoryCombo.addItems( poiCategories ) self.poiTypes = dict( self.poi.listPoitypes() ) poiTypes = [""] + list(self.poiTypes.keys()) poiTypes.sort() self.ui.filterPoiTypeCombo.addItems( poiTypes ) gemeentes = json.load( open(os.path.join(os.path.dirname(__file__), "data/gemeentenVL.json")) ) self.NIScodes= { n["Naam"] : n["Niscode"] for n in gemeentes } gemeenteNamen = [n["Naam"] for n in gemeentes] gemeenteNamen.sort() self.ui.filterPoiNIS.addItems( gemeenteNamen ) #connect when inet on self.ui.filterPoiThemeCombo.activated.connect(self.onThemeFilterChange) self.ui.filterPoiCategoryCombo.activated.connect(self.onCategorieFilterChange) self.firstShow = False else: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Waarschuwing "), QCoreApplication.translate("geopunt4QgisPoidialog", "Kan geen verbing maken met het internet."), level=Qgis.Warning, duration=3) def openHelp(self): webbrowser.open_new_tab("http://www.geopunt.be/voor-experts/geopunt-plug-ins/functionaliteiten/poi") def onZoekActivated(self): txt = self.ui.poiText.text() self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.ui.msgLbl.setText("") ##filters: poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[ poithemeText ] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ] else: poiType = "" NISText= self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=xyBox, theme=poitheme , category=poiCategorie, POItype=poiType ) else: self.poi.fetchPoi( txt, c=32, srs=4326 , maxModel=True, updateResults=True, bbox=None, theme=poitheme , category=poiCategorie, POItype=poiType, region=Niscode ) suggesties = self.poi.poiSuggestion() if type(suggesties) is list: #prevent sorting every time an item is added self.ui.resultLijst.setSortingEnabled(False) row =0 for sug in suggesties: id = QTableWidgetItem( sug[0], 0 ) theme = QTableWidgetItem( sug[1], 0 ) categorie = QTableWidgetItem( sug[2], 0 ) PoiType = QTableWidgetItem( sug[3], 0 ) naam = QTableWidgetItem( sug[4], 0 ) straat = QTableWidgetItem( sug[5], 0) huisnr = QTableWidgetItem( sug[6], 0) busnr = QTableWidgetItem( sug[7], 0) postcode = QTableWidgetItem( sug[8], 0) gemeente = QTableWidgetItem( sug[9], 0) self.ui.resultLijst.insertRow(row) self.ui.resultLijst.setItem(row, 0, id) self.ui.resultLijst.setItem(row, 1, theme) self.ui.resultLijst.setItem(row, 2, categorie) self.ui.resultLijst.setItem(row, 3, PoiType) self.ui.resultLijst.setItem(row, 4, naam) self.ui.resultLijst.setItem(row, 5, straat) self.ui.resultLijst.setItem(row, 6, huisnr) self.ui.resultLijst.setItem(row, 7, busnr) self.ui.resultLijst.setItem(row, 8, postcode) self.ui.resultLijst.setItem(row, 9, gemeente) row += 1 self.ui.resultLijst.setSortingEnabled(True) if self.poi.resultCount > 0: self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", "Aantal getoond: %s gevonden: %s" % ( self.ui.resultLijst.rowCount() , self.poi.resultCount ) )) elif self.poi.resultCount == 0: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Geen resultaten gevonden voor deze zoekopdracht"), "", level=Qgis.Info, duration=10) elif self.poi.resultCount < 0: self.bar.pushMessage(QCoreApplication.translate("geopunt4QgisPoidialog", "Het aantal gevonden kon niet worden bepaald, te complexe zoekopdracht"), "", level=Qgis.Info, duration=10) self.ui.msgLbl.setText(QCoreApplication.translate("geopunt4QgisPoidialog", "Aantal getoond: %s, aantal gevonden niet bepaald" % self.ui.resultLijst.rowCount() ) ) elif type( suggesties ) is str: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), suggesties, level=Qgis.Warning) else: self.bar.pushMessage("Error", QCoreApplication.translate("geopunt4QgisPoidialog","onbekende fout"), level=Qgis.Critical) def onZoomSelClicked(self): if not len( self.ui.resultLijst.selectedIndexes() ): self.bar.pushMessage("", QCoreApplication.translate("geopunt4QgisPoidialog", "Er zijn geen records geselecteerd"), level=Qgis.Warning ) return selPois = self._getSelectedPois() if len(selPois) <= 0 : self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog", "Merk op"), QCoreApplication.translate("geopunt4QgisPoidialog", "Er niets om naar te zoomen"), level=Qgis.Info, duration=3) elif len(selPois) >= 2: pts = [n['location']['points'][0]['Point']['coordinates'] for n in selPois ] bounds = self.gh.getBoundsOfPointArray(pts) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) elif len(selPois) == 1: x, y = selPois[0]['location']['points'][0]['Point']['coordinates'] bounds = self.gh.getBoundsOfPoint(x,y) self.gh.zoomtoRec(bounds[:2], bounds[2:4], 4326) def onSelectionChanged(self): selPois = self._getSelectedPois() canvas = self.iface.mapCanvas() self.clearGraphicsLayer() pts = [ self.gh.prjPtToMapCrs( n['location']['points'][0]['Point']['coordinates'], 4326) for n in selPois ] for pt in pts: x,y = pt m = QgsVertexMarker(canvas) self.graphicsLayer.append(m) m.setCenter(QgsPointXY(x,y)) m.setColor(QColor(255,255,0)) m.setIconSize(1) m.setIconType(QgsVertexMarker.ICON_BOX) m.setPenWidth(10) def onAddSelClicked(self): if not len( self.ui.resultLijst.selectedIndexes() ): self.bar.pushMessage("", QCoreApplication.translate("geopunt4QgisPoidialog", "Er zijn geen records geselecteerd"), level=Qgis.Warning ) return if not self.layernameValid(): return self.clearGraphicsLayer() pts = self._getSelectedPois() self.ph.save_pois_points( pts , layername=self.layerName, startFolder= os.path.join(self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self ) def onThemeFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poithemeID = self.poiThemes[ poithemeText ] poiCategories = [""] + [n[0] for n in self.poi.listPoiCategories(poithemeID)] poiCategories.sort() self.ui.filterPoiCategoryCombo.clear() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiCategoryCombo.addItems( poiCategories ) else: self.ui.filterPoiCategoryCombo.addItems([""] + list(self.poiCategories.keys())) self.ui.filterPoiTypeCombo.addItems([""] + list(self.poiTypes.keys())) def onCategorieFilterChange(self): poithemeText = self.ui.filterPoiThemeCombo.currentText() poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "" and poithemeText != "": poiCategorieID = self.poiCategories[ poiCategorieText ] poithemeID = self.poiThemes[ poithemeText ] poiTypes = [""] + [n[0] for n in self.poi.listPoitypes(poithemeID, poiCategorieID)] poiTypes.sort() self.ui.filterPoiTypeCombo.clear() self.ui.filterPoiTypeCombo.addItems( poiTypes ) def addMinModel(self): if not self.layernameValid(): return self.clearGraphicsLayer() txt = self.ui.poiText.text() poithemeText = self.ui.filterPoiThemeCombo.currentText() if poithemeText != "": poitheme = self.poiThemes[ poithemeText ] else: poitheme = "" poiCategorieText = self.ui.filterPoiCategoryCombo.currentText() if poiCategorieText != "": poiCategorie = self.poiCategories[ poiCategorieText ] else: poiCategorie = "" poiTypeText = self.ui.filterPoiTypeCombo.currentText() if poiTypeText!= "": poiType = self.poiTypes[ poiTypeText ] else: poiType = "" NISText= self.ui.filterPoiNIS.currentText() if NISText != "" and not self.ui.currentBoundsVink.isChecked(): Niscode = self.NIScodes[NISText] else: Niscode = "" cluster = self.ui.clusterCheck.isChecked() if self.ui.currentBoundsVink.isChecked(): bbox = self.iface.mapCanvas().extent() minX, minY = self.gh.prjPtFromMapCrs([bbox.xMinimum(),bbox.yMinimum()], 4326) maxX, maxY = self.gh.prjPtFromMapCrs([bbox.xMaximum(),bbox.yMaximum()], 4326) xyBox = [minX, minY, maxX, maxY] pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0, bbox= xyBox, theme= poitheme , category= poiCategorie, POItype= poiType, clustering= cluster ) else: pts= self.poi.fetchPoi( txt, c=1024, srs=4326 , maxModel=0, updateResults=0, bbox=None, theme= poitheme , category= poiCategorie, POItype= poiType, region= Niscode, clustering= cluster ) if type( pts ) == str: self.bar.pushMessage( QCoreApplication.translate("geopunt4QgisPoidialog","Waarschuwing"), pts, level=Qgis.Warning, duration=5) elif type( pts ) == list or type( pts ) == dict: self.ph.save_minPois_points(pts, layername=self.layerName, startFolder= os.path.join(self.startDir, self.layerName), saveToFile=self.saveToFile, sender=self ) self.close() def searchTxtChanged(self): txt= self.ui.poiText.text() if txt != "": msg = QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg meer punten toe") else: msg = QCoreApplication.translate("geopunt4QgisPoidialog", "Voeg alle punten toe" ) self.ui.addMinModelBtn.setText(msg) def _getSelectedPois(self): pois = self.poi.PoiResult selPois = [] selRows = set( [sel.row() for sel in self.ui.resultLijst.selectedIndexes()] ) for row in selRows: itemID = self.ui.resultLijst.item(row,0).text() selPois += [i for i in pois if i["id"] == itemID] return selPois def clearGraphicsLayer(self): for graphic in self.graphicsLayer: self.iface.mapCanvas().scene().removeItem(graphic) self.graphicsLayer = [] def layernameValid(self): if not hasattr(self, 'layerName'): layerName, accept = QInputDialog.getText(None, QCoreApplication.translate("geopunt4Qgis", 'Laag toevoegen'), QCoreApplication.translate("geopunt4Qgis", 'Geef een naam voor de laag op:') ) if accept == False: return False else: self.layerName = layerName return True def clean(self): self.bar.clearWidgets() self.ui.poiText.setText("") self.ui.msgLbl.setText("") self.ui.resultLijst.clearContents() self.ui.resultLijst.setRowCount(0) self.clearGraphicsLayer()
class DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QgsSettings() self.restoreGeometry( settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState( settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.toolBar.setIconSize(self.iface.iconSize()) self.tabs.currentChanged.connect(self.tabChanged) self.tree.selectedItemChanged.connect(self.itemChanged) self.tree.model().dataChanged.connect(self.iface.reloadConnections) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) # save the window state settings = QgsSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): with OverrideCursor(Qt.WaitCursor): try: if item is None: item = self.tree.currentItem() self.tree.refreshItem( item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) def itemChanged(self, item): with OverrideCursor(Qt.WaitCursor): try: self.reloadButtons() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): with OverrideCursor(Qt.WaitCursor): try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled( self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), Qgis.Info, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage( self.tr("Select the table you want export to file."), Qgis.Info, self.iface.messageTimeout()) return inLayer = table.toMapLayer() if inLayer.type() != QgsMapLayer.VectorLayer: self.infoBar.pushMessage( self.tr("Select a vector or a tabular layer you want export."), Qgis.Warning, self.iface.messageTimeout()) return from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage( self.tr( "No database selected or you are not connected to it."), Qgis.Info, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from .dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query ({0})").format(dbname) index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect( functools.partial(self.update_query_tab_name, index, dbname)) def runSqlLayerWindow(self, layer): from .dlg_sql_layer_window import DlgSqlLayerWindow query = DlgSqlLayerWindow(self.iface, layer, self) lname = layer.name() tabname = self.tr("Layer ({0})").format(lname) index = self.tabs.addTab(query, tabname) # self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: def invoke_callback(x): return self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName( ).endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ with OverrideCursor(Qt.WaitCursor): try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[ i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: self.tabs.removeTab(index) widget.deleteLater() def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton( i, QTabBar.RightSide) else tabbar.tabButton( i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget(self.tr("Tree"), self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar(self.tr("Default"), self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction( QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction( QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL Window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction( QIcon(":/db_manager/actions/import"), QApplication.translate("DBManager", "&Import Layer/File…"), self.importActionSlot) self.actionExport = self.menuTable.addAction( QIcon(":/db_manager/actions/export"), QApplication.translate("DBManager", "&Export to File…"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)
class ModelerDialog(BASE, WIDGET): ALG_ITEM = 'ALG_ITEM' PROVIDER_ITEM = 'PROVIDER_ITEM' GROUP_ITEM = 'GROUP_ITEM' NAME_ROLE = Qt.UserRole TAG_ROLE = Qt.UserRole + 1 TYPE_ROLE = Qt.UserRole + 2 CANVAS_SIZE = 4000 update_model = pyqtSignal() def __init__(self, model=None): super().__init__(None) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi(self) self._variables_scope = None # LOTS of bug reports when we include the dock creation in the UI file # see e.g. #16428, #19068 # So just roll it all by hand......! self.propertiesDock = QgsDockWidget(self) self.propertiesDock.setFeatures( QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.propertiesDock.setObjectName("propertiesDock") propertiesDockContents = QWidget() self.verticalDockLayout_1 = QVBoxLayout(propertiesDockContents) self.verticalDockLayout_1.setContentsMargins(0, 0, 0, 0) self.verticalDockLayout_1.setSpacing(0) self.scrollArea_1 = QgsScrollArea(propertiesDockContents) sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scrollArea_1.sizePolicy().hasHeightForWidth()) self.scrollArea_1.setSizePolicy(sizePolicy) self.scrollArea_1.setFocusPolicy(Qt.WheelFocus) self.scrollArea_1.setFrameShape(QFrame.NoFrame) self.scrollArea_1.setFrameShadow(QFrame.Plain) self.scrollArea_1.setWidgetResizable(True) self.scrollAreaWidgetContents_1 = QWidget() self.gridLayout = QGridLayout(self.scrollAreaWidgetContents_1) self.gridLayout.setContentsMargins(6, 6, 6, 6) self.gridLayout.setSpacing(4) self.label_1 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_1, 0, 0, 1, 1) self.textName = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textName, 0, 1, 1, 1) self.label_2 = QLabel(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1) self.textGroup = QLineEdit(self.scrollAreaWidgetContents_1) self.gridLayout.addWidget(self.textGroup, 1, 1, 1, 1) self.label_1.setText(self.tr("Name")) self.textName.setToolTip(self.tr("Enter model name here")) self.label_2.setText(self.tr("Group")) self.textGroup.setToolTip(self.tr("Enter group name here")) self.scrollArea_1.setWidget(self.scrollAreaWidgetContents_1) self.verticalDockLayout_1.addWidget(self.scrollArea_1) self.propertiesDock.setWidget(propertiesDockContents) self.propertiesDock.setWindowTitle(self.tr("Model Properties")) self.inputsDock = QgsDockWidget(self) self.inputsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.inputsDock.setObjectName("inputsDock") self.inputsDockContents = QWidget() self.verticalLayout_3 = QVBoxLayout(self.inputsDockContents) self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) self.scrollArea_2 = QgsScrollArea(self.inputsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_2.sizePolicy().hasHeightForWidth()) self.scrollArea_2.setSizePolicy(sizePolicy) self.scrollArea_2.setFocusPolicy(Qt.WheelFocus) self.scrollArea_2.setFrameShape(QFrame.NoFrame) self.scrollArea_2.setFrameShadow(QFrame.Plain) self.scrollArea_2.setWidgetResizable(True) self.scrollAreaWidgetContents_2 = QWidget() self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents_2) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setSpacing(0) self.inputsTree = QTreeWidget(self.scrollAreaWidgetContents_2) self.inputsTree.setAlternatingRowColors(True) self.inputsTree.header().setVisible(False) self.verticalLayout.addWidget(self.inputsTree) self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) self.verticalLayout_3.addWidget(self.scrollArea_2) self.inputsDock.setWidget(self.inputsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.inputsDock) self.inputsDock.setWindowTitle(self.tr("Inputs")) self.algorithmsDock = QgsDockWidget(self) self.algorithmsDock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.algorithmsDock.setObjectName("algorithmsDock") self.algorithmsDockContents = QWidget() self.verticalLayout_4 = QVBoxLayout(self.algorithmsDockContents) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.scrollArea_3 = QgsScrollArea(self.algorithmsDockContents) sizePolicy.setHeightForWidth(self.scrollArea_3.sizePolicy().hasHeightForWidth()) self.scrollArea_3.setSizePolicy(sizePolicy) self.scrollArea_3.setFocusPolicy(Qt.WheelFocus) self.scrollArea_3.setFrameShape(QFrame.NoFrame) self.scrollArea_3.setFrameShadow(QFrame.Plain) self.scrollArea_3.setWidgetResizable(True) self.scrollAreaWidgetContents_3 = QWidget() self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents_3) self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) self.verticalLayout_2.setSpacing(4) self.searchBox = QgsFilterLineEdit(self.scrollAreaWidgetContents_3) self.verticalLayout_2.addWidget(self.searchBox) self.algorithmTree = QgsProcessingToolboxTreeView(None, QgsApplication.processingRegistry()) self.algorithmTree.setAlternatingRowColors(True) self.algorithmTree.header().setVisible(False) self.verticalLayout_2.addWidget(self.algorithmTree) self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3) self.verticalLayout_4.addWidget(self.scrollArea_3) self.algorithmsDock.setWidget(self.algorithmsDockContents) self.addDockWidget(Qt.DockWidgetArea(1), self.algorithmsDock) self.algorithmsDock.setWindowTitle(self.tr("Algorithms")) self.searchBox.setToolTip(self.tr("Enter algorithm name to filter list")) self.searchBox.setShowSearchIcon(True) self.variables_dock = QgsDockWidget(self) self.variables_dock.setFeatures(QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable) self.variables_dock.setObjectName("variablesDock") self.variables_dock_contents = QWidget() vl_v = QVBoxLayout() vl_v.setContentsMargins(0, 0, 0, 0) self.variables_editor = QgsVariableEditorWidget() vl_v.addWidget(self.variables_editor) self.variables_dock_contents.setLayout(vl_v) self.variables_dock.setWidget(self.variables_dock_contents) self.addDockWidget(Qt.DockWidgetArea(1), self.variables_dock) self.variables_dock.setWindowTitle(self.tr("Variables")) self.addDockWidget(Qt.DockWidgetArea(1), self.propertiesDock) self.tabifyDockWidget(self.propertiesDock, self.variables_dock) self.variables_editor.scopeChanged.connect(self.variables_changed) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.centralWidget().layout().insertWidget(0, self.bar) try: self.setDockOptions(self.dockOptions() | QMainWindow.GroupedDragging) except: pass if iface is not None: self.mToolbar.setIconSize(iface.iconSize()) self.setStyleSheet(iface.mainWindow().styleSheet()) self.toolbutton_export_to_script = QToolButton() self.toolbutton_export_to_script.setPopupMode(QToolButton.InstantPopup) self.export_to_script_algorithm_action = QAction(QCoreApplication.translate('ModelerDialog', 'Export as Script Algorithm…')) self.toolbutton_export_to_script.addActions([self.export_to_script_algorithm_action]) self.mToolbar.insertWidget(self.mActionExportImage, self.toolbutton_export_to_script) self.export_to_script_algorithm_action.triggered.connect(self.export_as_script_algorithm) self.mActionOpen.setIcon( QgsApplication.getThemeIcon('/mActionFileOpen.svg')) self.mActionSave.setIcon( QgsApplication.getThemeIcon('/mActionFileSave.svg')) self.mActionSaveAs.setIcon( QgsApplication.getThemeIcon('/mActionFileSaveAs.svg')) self.mActionSaveInProject.setIcon( QgsApplication.getThemeIcon('/mAddToProject.svg')) self.mActionZoomActual.setIcon( QgsApplication.getThemeIcon('/mActionZoomActual.svg')) self.mActionZoomIn.setIcon( QgsApplication.getThemeIcon('/mActionZoomIn.svg')) self.mActionZoomOut.setIcon( QgsApplication.getThemeIcon('/mActionZoomOut.svg')) self.mActionExportImage.setIcon( QgsApplication.getThemeIcon('/mActionSaveMapAsImage.svg')) self.mActionZoomToItems.setIcon( QgsApplication.getThemeIcon('/mActionZoomFullExtent.svg')) self.mActionExportPdf.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPDF.svg')) self.mActionExportSvg.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsSVG.svg')) self.toolbutton_export_to_script.setIcon( QgsApplication.getThemeIcon('/mActionSaveAsPython.svg')) self.mActionEditHelp.setIcon( QgsApplication.getThemeIcon('/mActionEditHelpContent.svg')) self.mActionRun.setIcon( QgsApplication.getThemeIcon('/mActionStart.svg')) self.addDockWidget(Qt.LeftDockWidgetArea, self.propertiesDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputsDock) self.addDockWidget(Qt.LeftDockWidgetArea, self.algorithmsDock) self.tabifyDockWidget(self.inputsDock, self.algorithmsDock) self.inputsDock.raise_() self.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowCloseButtonHint) settings = QgsSettings() self.restoreState(settings.value("/Processing/stateModeler", QByteArray())) self.restoreGeometry(settings.value("/Processing/geometryModeler", QByteArray())) self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.view.setScene(self.scene) self.view.setAcceptDrops(True) self.view.ensureVisible(0, 0, 10, 10) self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) def _dragEnterEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.acceptProposedAction() else: event.ignore() def _dropEvent(event): def alg_dropped(algorithm_id, pos): alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm_id) if alg is not None: self._addAlgorithm(alg, pos) else: assert False, algorithm_id def input_dropped(id, pos): if id in [param.id() for param in QgsApplication.instance().processingRegistry().parameterTypes()]: self.addInputOfType(itemId, pos) if event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): data = event.mimeData().data('application/x-vnd.qgis.qgis.algorithmid') stream = QDataStream(data, QIODevice.ReadOnly) algorithm_id = stream.readQString() QTimer.singleShot(0, lambda id=algorithm_id, pos=self.view.mapToScene(event.pos()): alg_dropped(id, pos)) event.accept() elif event.mimeData().hasText(): itemId = event.mimeData().text() QTimer.singleShot(0, lambda id=itemId, pos=self.view.mapToScene(event.pos()): input_dropped(id, pos)) event.accept() else: event.ignore() def _dragMoveEvent(event): if event.mimeData().hasText() or event.mimeData().hasFormat('application/x-vnd.qgis.qgis.algorithmid'): event.accept() else: event.ignore() def _wheelEvent(event): self.view.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) # "Normal" mouse has an angle delta of 120, precision mouses provide data # faster, in smaller steps factor = 1.0 + (factor - 1.0) / 120.0 * abs(event.angleDelta().y()) if (event.modifiers() == Qt.ControlModifier): factor = 1.0 + (factor - 1.0) / 20.0 if event.angleDelta().y() < 0: factor = 1 / factor self.view.scale(factor, factor) def _enterEvent(e): QGraphicsView.enterEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mouseReleaseEvent(e): QGraphicsView.mouseReleaseEvent(self.view, e) self.view.viewport().setCursor(Qt.ArrowCursor) def _mousePressEvent(e): if e.button() == Qt.MidButton: self.previousMousePos = e.pos() else: QGraphicsView.mousePressEvent(self.view, e) def _mouseMoveEvent(e): if e.buttons() == Qt.MidButton: offset = self.previousMousePos - e.pos() self.previousMousePos = e.pos() self.view.verticalScrollBar().setValue(self.view.verticalScrollBar().value() + offset.y()) self.view.horizontalScrollBar().setValue(self.view.horizontalScrollBar().value() + offset.x()) else: QGraphicsView.mouseMoveEvent(self.view, e) self.view.setDragMode(QGraphicsView.ScrollHandDrag) self.view.dragEnterEvent = _dragEnterEvent self.view.dropEvent = _dropEvent self.view.dragMoveEvent = _dragMoveEvent self.view.wheelEvent = _wheelEvent self.view.enterEvent = _enterEvent self.view.mousePressEvent = _mousePressEvent self.view.mouseMoveEvent = _mouseMoveEvent def _mimeDataInput(items): mimeData = QMimeData() text = items[0].data(0, Qt.UserRole) mimeData.setText(text) return mimeData self.inputsTree.mimeData = _mimeDataInput self.inputsTree.setDragDropMode(QTreeWidget.DragOnly) self.inputsTree.setDropIndicatorShown(True) self.algorithms_model = ModelerToolboxModel(self, QgsApplication.processingRegistry()) self.algorithmTree.setToolboxProxyModel(self.algorithms_model) self.algorithmTree.setDragDropMode(QTreeWidget.DragOnly) self.algorithmTree.setDropIndicatorShown(True) filters = QgsProcessingToolboxProxyModel.Filters(QgsProcessingToolboxProxyModel.FilterModeler) if ProcessingConfig.getSetting(ProcessingConfig.SHOW_ALGORITHMS_KNOWN_ISSUES): filters |= QgsProcessingToolboxProxyModel.FilterShowKnownIssues self.algorithmTree.setFilters(filters) if hasattr(self.searchBox, 'setPlaceholderText'): self.searchBox.setPlaceholderText(QCoreApplication.translate('ModelerDialog', 'Search…')) if hasattr(self.textName, 'setPlaceholderText'): self.textName.setPlaceholderText(self.tr('Enter model name here')) if hasattr(self.textGroup, 'setPlaceholderText'): self.textGroup.setPlaceholderText(self.tr('Enter group name here')) # Connect signals and slots self.inputsTree.doubleClicked.connect(self.addInput) self.searchBox.textChanged.connect(self.algorithmTree.setFilterString) self.algorithmTree.doubleClicked.connect(self.addAlgorithm) # Ctrl+= should also trigger a zoom in action ctrlEquals = QShortcut(QKeySequence("Ctrl+="), self) ctrlEquals.activated.connect(self.zoomIn) self.mActionOpen.triggered.connect(self.openModel) self.mActionSave.triggered.connect(self.save) self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveInProject.triggered.connect(self.saveInProject) self.mActionZoomIn.triggered.connect(self.zoomIn) self.mActionZoomOut.triggered.connect(self.zoomOut) self.mActionZoomActual.triggered.connect(self.zoomActual) self.mActionZoomToItems.triggered.connect(self.zoomToItems) self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportPdf.triggered.connect(self.exportAsPdf) self.mActionExportSvg.triggered.connect(self.exportAsSvg) #self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionRun.triggered.connect(self.runModel) if model is not None: self.model = model.create() self.model.setSourceFilePath(model.sourceFilePath()) self.textGroup.setText(self.model.group()) self.textName.setText(self.model.displayName()) self.repaintModel() else: self.model = QgsProcessingModelAlgorithm() self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.update_variables_gui() self.fillInputsTree() self.view.centerOn(0, 0) self.help = None self.hasChanged = False def closeEvent(self, evt): settings = QgsSettings() settings.setValue("/Processing/stateModeler", self.saveState()) settings.setValue("/Processing/geometryModeler", self.saveGeometry()) if self.hasChanged: ret = QMessageBox.question( self, self.tr('Save Model?'), self.tr('There are unsaved changes in this model. Do you want to keep those?'), QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard, QMessageBox.Cancel) if ret == QMessageBox.Save: self.saveModel(False) evt.accept() elif ret == QMessageBox.Discard: evt.accept() else: evt.ignore() else: evt.accept() def editHelp(self): alg = self.model dlg = HelpEditionDialog(alg) dlg.exec_() if dlg.descriptions: self.model.setHelpContent(dlg.descriptions) self.hasChanged = True def update_variables_gui(self): variables_scope = QgsExpressionContextScope(self.tr('Model Variables')) for k, v in self.model.variables().items(): variables_scope.setVariable(k, v) variables_context = QgsExpressionContext() variables_context.appendScope(variables_scope) self.variables_editor.setContext(variables_context) self.variables_editor.setEditableScopeIndex(0) def variables_changed(self): self.model.setVariables(self.variables_editor.variablesInActiveScope()) def runModel(self): if len(self.model.childAlgorithms()) == 0: self.bar.pushMessage("", self.tr("Model doesn't contain any algorithm and/or parameter and can't be executed"), level=Qgis.Warning, duration=5) return dlg = AlgorithmDialog(self.model.create(), parent=iface.mainWindow()) dlg.exec_() def save(self): self.saveModel(False) def saveAs(self): self.saveModel(True) def saveInProject(self): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) self.model.setSourceFilePath(None) project_provider = QgsApplication.processingRegistry().providerById(PROJECT_PROVIDER_ID) project_provider.add_model(self.model) self.update_model.emit() self.bar.pushMessage("", self.tr("Model was saved inside current project"), level=Qgis.Success, duration=5) self.hasChanged = False QgsProject.instance().setDirty(True) def zoomIn(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomOut(self): self.view.setTransformationAnchor(QGraphicsView.NoAnchor) point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) settings = QgsSettings() factor = settings.value('/qgis/zoom_favor', 2.0) factor = 1 / factor self.view.scale(factor, factor) self.view.centerOn(point) self.repaintModel() def zoomActual(self): point = self.view.mapToScene(QPoint(self.view.viewport().width() / 2, self.view.viewport().height() / 2)) self.view.resetTransform() self.view.scale(QgsApplication.desktop().logicalDpiX() / 96, QgsApplication.desktop().logicalDpiX() / 96) self.view.centerOn(point) def zoomToItems(self): totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) self.view.fitInView(totalRect, Qt.KeepAspectRatio) def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", self.tr("Successfully exported model as image to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPdf(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As PDF'), '', self.tr('PDF files (*.pdf *.PDF)')) if not filename: return if not filename.lower().endswith('.pdf'): filename += '.pdf' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) printerRect = QRectF(0, 0, totalRect.width(), totalRect.height()) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(filename) printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel) printer.setFullPage(True) painter = QPainter(printer) self.scene.render(painter, printerRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as PDF to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsSvg(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As SVG'), '', self.tr('SVG files (*.svg *.SVG)')) if not filename: return if not filename.lower().endswith('.svg'): filename += '.svg' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) svgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) svg = QSvgGenerator() svg.setFileName(filename) svg.setSize(QSize(totalRect.width(), totalRect.height())) svg.setViewBox(svgRect) svg.setTitle(self.model.displayName()) painter = QPainter(svg) self.scene.render(painter, svgRect, totalRect) painter.end() self.bar.pushMessage("", self.tr("Successfully exported model as SVG to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True) def exportAsPython(self): filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Python Script'), '', self.tr('Processing scripts (*.py *.PY)')) if not filename: return if not filename.lower().endswith('.py'): filename += '.py' text = self.model.asPythonCode() with codecs.open(filename, 'w', encoding='utf-8') as fout: fout.write(text) self.bar.pushMessage("", self.tr("Successfully exported model as python script to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) def can_save(self): """ Tests whether a model can be saved, or if it is not yet valid :return: bool """ if str(self.textName.text()).strip() == '': self.bar.pushWarning( "", self.tr('Please a enter model name before saving') ) return False return True def saveModel(self, saveAs): if not self.can_save(): return self.model.setName(str(self.textName.text())) self.model.setGroup(str(self.textGroup.text())) if self.model.sourceFilePath() and not saveAs: filename = self.model.sourceFilePath() else: filename, filter = QFileDialog.getSaveFileName(self, self.tr('Save Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: if not filename.endswith('.model3'): filename += '.model3' self.model.setSourceFilePath(filename) if filename: if not self.model.toFile(filename): if saveAs: QMessageBox.warning(self, self.tr('I/O error'), self.tr('Unable to save edits. Reason:\n {0}').format(str(sys.exc_info()[1]))) else: QMessageBox.warning(self, self.tr("Can't save model"), QCoreApplication.translate('QgsPluginInstallerInstallingDialog', ( "This model can't be saved in its original location (probably you do not " "have permission to do it). Please, use the 'Save as…' option.")) ) return self.update_model.emit() if saveAs: self.bar.pushMessage("", self.tr("Model was correctly saved to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) else: self.bar.pushMessage("", self.tr("Model was correctly saved"), level=Qgis.Success, duration=5) self.hasChanged = False def openModel(self): filename, selected_filter = QFileDialog.getOpenFileName(self, self.tr('Open Model'), ModelerUtils.modelsFolders()[0], self.tr('Processing models (*.model3 *.MODEL3)')) if filename: self.loadModel(filename) def loadModel(self, filename): alg = QgsProcessingModelAlgorithm() if alg.fromFile(filename): self.model = alg self.model.setProvider(QgsApplication.processingRegistry().providerById('model')) self.textGroup.setText(alg.group()) self.textName.setText(alg.name()) self.repaintModel() self.update_variables_gui() self.view.centerOn(0, 0) self.hasChanged = False else: QgsMessageLog.logMessage(self.tr('Could not load model {0}').format(filename), self.tr('Processing'), Qgis.Critical) QMessageBox.critical(self, self.tr('Open Model'), self.tr('The selected model could not be loaded.\n' 'See the log for more information.')) def repaintModel(self, controls=True): self.scene = ModelerScene(self, dialog=self) self.scene.setSceneRect(QRectF(0, 0, self.CANVAS_SIZE, self.CANVAS_SIZE)) self.scene.paintModel(self.model, controls) self.view.setScene(self.scene) def addInput(self): item = self.inputsTree.currentItem() param = item.data(0, Qt.UserRole) self.addInputOfType(param) def addInputOfType(self, paramType, pos=None): dlg = ModelerParameterDefinitionDialog(self.model, paramType) dlg.exec_() if dlg.param is not None: if pos is None: pos = self.getPositionForParameterItem() if isinstance(pos, QPoint): pos = QPointF(pos) component = QgsProcessingModelParameter(dlg.param.name()) component.setDescription(dlg.param.name()) component.setPosition(pos) self.model.addModelParameter(dlg.param, component) self.repaintModel() # self.view.ensureVisible(self.scene.getLastParameterItem()) self.hasChanged = True def getPositionForParameterItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if len(self.model.parameterComponents()) > 0: maxX = max([i.position().x() for i in list(self.model.parameterComponents().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) else: newX = MARGIN + BOX_WIDTH / 2 return QPointF(newX, MARGIN + BOX_HEIGHT / 2) def fillInputsTree(self): icon = QIcon(os.path.join(pluginPath, 'images', 'input.svg')) parametersItem = QTreeWidgetItem() parametersItem.setText(0, self.tr('Parameters')) sortedParams = sorted(QgsApplication.instance().processingRegistry().parameterTypes(), key=lambda pt: pt.name()) for param in sortedParams: if param.flags() & QgsProcessingParameterType.ExposeToModeler: paramItem = QTreeWidgetItem() paramItem.setText(0, param.name()) paramItem.setData(0, Qt.UserRole, param.id()) paramItem.setIcon(0, icon) paramItem.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) paramItem.setToolTip(0, param.description()) parametersItem.addChild(paramItem) self.inputsTree.addTopLevelItem(parametersItem) parametersItem.setExpanded(True) def addAlgorithm(self): algorithm = self.algorithmTree.selectedAlgorithm() if algorithm is not None: alg = QgsApplication.processingRegistry().createAlgorithmById(algorithm.id()) self._addAlgorithm(alg) def _addAlgorithm(self, alg, pos=None): dlg = ModelerParametersDialog(alg, self.model) if dlg.exec_(): alg = dlg.createAlgorithm() if pos is None: alg.setPosition(self.getPositionForAlgorithmItem()) else: alg.setPosition(pos) from processing.modeler.ModelerGraphicItem import ModelerGraphicItem for i, out in enumerate(alg.modelOutputs()): alg.modelOutput(out).setPosition(alg.position() + QPointF(ModelerGraphicItem.BOX_WIDTH, (i + 1.5) * ModelerGraphicItem.BOX_HEIGHT)) self.model.addChildAlgorithm(alg) self.repaintModel() self.hasChanged = True def getPositionForAlgorithmItem(self): MARGIN = 20 BOX_WIDTH = 200 BOX_HEIGHT = 80 if self.model.childAlgorithms(): maxX = max([alg.position().x() for alg in list(self.model.childAlgorithms().values())]) maxY = max([alg.position().y() for alg in list(self.model.childAlgorithms().values())]) newX = min(MARGIN + BOX_WIDTH + maxX, self.CANVAS_SIZE - BOX_WIDTH) newY = min(MARGIN + BOX_HEIGHT + maxY, self.CANVAS_SIZE - BOX_HEIGHT) else: newX = MARGIN + BOX_WIDTH / 2 newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2 return QPointF(newX, newY) def export_as_script_algorithm(self): dlg = ScriptEditorDialog(None) dlg.editor.setText('\n'.join(self.model.asPythonCode(QgsProcessing.PythonQgsProcessingAlgorithmSubclass, 4))) dlg.show()
class DialogExportData(QDialog, DIALOG_UI): on_result = pyqtSignal( bool) # whether the tool was run successfully or not ValidExtensions = ['xtf', 'itf', 'gml', 'xml'] current_row_schema = 0 def __init__(self, iface, conn_manager, context, parent=None): QDialog.__init__(self, parent) self.setupUi(self) QgsGui.instance().enableAutoGeometryRestore(self) self.iface = iface self.conn_manager = conn_manager self.db_source = context.get_db_sources()[0] self.db = self.conn_manager.get_db_connector_from_source( self.db_source) self.logger = Logger() self.app = AppInterface() self.java_dependency = JavaDependency() self.java_dependency.download_dependency_completed.connect( self.download_java_complete) self.java_dependency.download_dependency_progress_changed.connect( self.download_java_progress_change) self._dbs_supported = ConfigDBsSupported() self._running_tool = False # There may be 1 case where we need to emit a db_connection_changed from the Export Data dialog: # 1) Connection Settings was opened and the DB conn was changed. self._db_was_changed = False # To postpone calling refresh gui until we close this dialog instead of settings # Similarly, we could call a refresh on layers and relations cache in 1 case: # 1) If the ED dialog was called for the COLLECTED source: opening Connection Settings and changing the DB # connection. self._schedule_layers_and_relations_refresh = False # We need bar definition above calling clear_messages self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) self.xtf_file_browse_button.clicked.connect( make_save_file_selector( self.xtf_file_line_edit, title=QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File"), file_filter=QCoreApplication.translate( "DialogExportData", "XTF Transfer File (*.xtf);;Interlis 1 Transfer File (*.itf);;XML (*.xml);;GML (*.gml)" ), extension='.xtf', extensions=['.' + ext for ext in self.ValidExtensions])) self.xtf_file_browse_button.clicked.connect( self.xtf_browser_opened_to_true) self.xtf_browser_was_opened = False self.validators = Validators() fileValidator = FileValidator( pattern=['*.' + ext for ext in self.ValidExtensions], allow_non_existing=True) self.xtf_file_line_edit.setPlaceholderText( QCoreApplication.translate("DialogExportData", "[Name of the XTF to be created]")) self.xtf_file_line_edit.setValidator(fileValidator) self.xtf_file_line_edit.textChanged.connect( self.validators.validate_line_edits) self.xtf_file_line_edit.textChanged.connect( self.xtf_browser_opened_to_false) self.xtf_file_line_edit.textChanged.emit( self.xtf_file_line_edit.text()) self.connection_setting_button.clicked.connect(self.show_settings) self.connection_setting_button.setText( QCoreApplication.translate("DialogExportData", "Connection Settings")) # LOG self.log_config.setTitle( QCoreApplication.translate("DialogExportData", "Show log")) self.log_config.setFlat(True) self.buttonBox.accepted.disconnect() self.buttonBox.accepted.connect(self.accepted) self.buttonBox.clear() self.buttonBox.addButton(QDialogButtonBox.Cancel) self._accept_button = self.buttonBox.addButton( QCoreApplication.translate("DialogExportData", "Export data"), QDialogButtonBox.AcceptRole) self.buttonBox.addButton(QDialogButtonBox.Help) self.buttonBox.helpRequested.connect(self.show_help) self.update_connection_info() self.update_model_names() self.restore_configuration() def update_connection_info(self): db_description = self.db.get_description_conn_string() if db_description: self.db_connect_label.setText(db_description) self.db_connect_label.setToolTip(self.db.get_display_conn_string()) self._accept_button.setEnabled(True) else: self.db_connect_label.setText( QCoreApplication.translate("DialogExportData", "The database is not defined!")) self.db_connect_label.setToolTip('') self._accept_button.setEnabled(False) def update_model_names(self): self.export_models_qmodel = QStandardItemModel() model_names = self.db.get_models() if model_names: for model in LADMColModelRegistry().supported_models(): if not model.hidden() and model.full_name() in model_names: item = QStandardItem(model.full_alias()) item.setData(model.full_name(), Qt.UserRole) item.setCheckable(False) item.setEditable(False) self.export_models_qmodel.appendRow(item) self.export_models_list_view.setModel(self.export_models_qmodel) def reject(self): if self._running_tool: QMessageBox.information( self, QCoreApplication.translate("DialogExportData", "Warning"), QCoreApplication.translate( "DialogExportData", "The Export Data tool is still running. Please wait until it finishes." )) else: self.close_dialog() def close_dialog(self): """ We use this method to be safe when emitting the db_connection_changed, otherwise we could trigger slots that unload the plugin, destroying dialogs and thus, leading to crashes. """ if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.connect( self.app.core.cache_layers_and_relations) if self._db_was_changed: # If the db was changed, it implies it complies with ladm_col, hence the second parameter self.conn_manager.db_connection_changed.emit( self.db, True, self.db_source) if self._schedule_layers_and_relations_refresh: self.conn_manager.db_connection_changed.disconnect( self.app.core.cache_layers_and_relations) self.logger.info(__name__, "Dialog closed.") self.done(QDialog.Accepted) def get_ili_models(self): ili_models = list() for index in range(self.export_models_qmodel.rowCount()): item = self.export_models_qmodel.item(index) ili_models.append(item.data(Qt.UserRole)) return ili_models def show_settings(self): # We only need those tabs related to Model Baker/ili2db operations dlg = SettingsDialog(self.conn_manager, parent=self) dlg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Source DB Connection Settings")) dlg.show_tip( QCoreApplication.translate( "DialogExportData", "Configure which DB you want to export data from.")) dlg.set_db_source(self.db_source) dlg.set_tab_pages_list( [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX]) # Connect signals (DBUtils, Core) dlg.db_connection_changed.connect(self.db_connection_changed) if self.db_source == COLLECTED_DB_SOURCE: self._schedule_layers_and_relations_refresh = True dlg.set_action_type(EnumDbActionType.EXPORT) if dlg.exec_(): self.db = dlg.get_db_connection() self.update_model_names() self.update_connection_info() def db_connection_changed(self, db, ladm_col_db, db_source): self._db_was_changed = True self.clear_messages() def accepted(self): self._running_tool = True self.txtStdout.clear() self.progress_bar.setValue(0) self.bar.clearWidgets() java_home_set = self.java_dependency.set_java_home() if not java_home_set: message_java = QCoreApplication.translate( "DialogExportData", """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION) self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(message_java) self.java_dependency.get_java_on_demand() self.disable() return ili2db = Ili2DB() configuration = self.update_configuration(ili2db) if configuration.disable_validation: # If data validation at export is disabled, we ask for confirmation self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Question) self.msg.setText( QCoreApplication.translate( "DialogExportData", "Are you sure you want to export your data without validation?" )) self.msg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Export XTF without validation?")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) res = self.msg.exec_() if res == QMessageBox.No: self._running_tool = False return if not self.xtf_file_line_edit.validator().validate( configuration.xtffile, 0)[0] == QValidator.Acceptable: self._running_tool = False message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid XTF file before exporting data.") self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.xtf_file_line_edit.setFocus() return if not self.get_ili_models(): self._running_tool = False message_error = QCoreApplication.translate( "DialogExportData", "Please set a valid schema to export. This schema does not have information to export." ) self.txtStdout.setText(message_error) self.show_message(message_error, Qgis.Warning) self.export_models_list_view.setFocus() return # If xtf browser was opened and the file exists, the user already chose # to overwrite the file if os.path.isfile(self.xtf_file_line_edit.text().strip() ) and not self.xtf_browser_was_opened: self.msg = QMessageBox() self.msg.setIcon(QMessageBox.Warning) self.msg.setText( QCoreApplication.translate( "DialogExportData", "{filename} already exists.\nDo you want to replace it?"). format(filename=os.path.basename( self.xtf_file_line_edit.text().strip()))) self.msg.setWindowTitle( QCoreApplication.translate("DialogExportData", "Save in XTF Transfer File")) self.msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msg_box = self.msg.exec_() if msg_box == QMessageBox.No: self._running_tool = False return self.progress_bar.show() self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self._connect_ili2db_signals(ili2db) self.save_configuration(configuration) res, msg = ili2db.export(self.db, configuration) self._disconnect_ili2db_signals(ili2db) self._running_tool = False self.progress_bar.setValue(25) if res: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) self.progress_bar.setValue(100) else: # Since the export was not successful, we'll try to remove any temp XTF generated if os.path.exists(configuration.xtffile): try: os.remove(configuration.xtffile) except: pass message_type = Qgis.Success if res else Qgis.Warning self.show_message(msg, message_type) # Inform other classes whether the execution was successful or not self.on_result.emit(res) def download_java_complete(self): self.accepted() def download_java_progress_change(self, progress): self.progress_bar.setValue(progress / 2) if (progress % 20) == 0: self.txtStdout.append('...') def save_configuration(self, configuration): settings = QSettings() settings.setValue( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export', configuration.xtffile) settings.setValue('Asistente-LADM-COL/QgisModelBaker/show_log', not self.log_config.isCollapsed()) def restore_configuration(self): settings = QSettings() self.xtf_file_line_edit.setText( settings.value( 'Asistente-LADM-COL/QgisModelBaker/ili2pg/xtffile_export')) # Show log value_show_log = settings.value( 'Asistente-LADM-COL/QgisModelBaker/show_log', False, type=bool) self.log_config.setCollapsed(not value_show_log) # set model repository # if there is no option by default use online model repository custom_model_is_checked = self.app.settings.custom_models if custom_model_is_checked: self.custom_model_directories = self.app.settings.custom_model_dirs def update_configuration(self, ili2db: Ili2DB): """ Get the configuration that is updated with the user configuration changes on the dialog. :return: Configuration """ disable_validation = \ not QSettings().value('Asistente-LADM-COL/models/validate_data_importing_exporting', True, bool) configuration = ili2db.get_export_configuration( self.db, self.xtf_file_line_edit.text().strip(), disable_validation=disable_validation) # TODO this is different to ili2db.py if self.get_ili_models(): configuration.ilimodels = ';'.join(self.get_ili_models()) return configuration def print_info(self, text, text_color='#000000', clear=False): self.txtStdout.setTextColor(QColor(text_color)) self.txtStdout.append(text) QCoreApplication.processEvents() def on_stderr(self, text): color_log_text(text, self.txtStdout) self.advance_progress_bar_by_text(text) def on_process_started(self, command): self.disable() self.txtStdout.setTextColor(QColor('#000000')) self.txtStdout.clear() self.txtStdout.setText(command) QCoreApplication.processEvents() def on_process_finished(self, exit_code, result): color = '#004905' if exit_code == 0 else '#aa2222' self.txtStdout.setTextColor(QColor(color)) self.txtStdout.append( QCoreApplication.translate("DialogExportData", "Finished ({})").format(exit_code)) if result == iliexporter.Exporter.SUCCESS: self.buttonBox.clear() self.buttonBox.setEnabled(True) self.buttonBox.addButton(QDialogButtonBox.Close) else: self.enable() # Open log if self.log_config.isCollapsed(): self.log_config.setCollapsed(False) def advance_progress_bar_by_text(self, text): if text.strip() == 'Info: compile models...': self.progress_bar.setValue(50) QCoreApplication.processEvents() elif text.strip() == 'Info: process data...': self.progress_bar.setValue(55) QCoreApplication.processEvents() elif text.strip() == 'Info: first validation pass...': self.progress_bar.setValue(70) QCoreApplication.processEvents() elif text.strip() == 'Info: second validation pass...': self.progress_bar.setValue(85) QCoreApplication.processEvents() def clear_messages(self): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.txtStdout.clear() # Clear previous log messages self.progress_bar.setValue(0) # Initialize progress bar def show_help(self): show_plugin_help("export_data") def disable(self): self.source_config.setEnabled(False) self.target_config.setEnabled(False) self.buttonBox.setEnabled(False) def enable(self): self.source_config.setEnabled(True) self.target_config.setEnabled(True) self.buttonBox.setEnabled(True) def show_message(self, message, level): if level == Qgis.Warning: self.enable() self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage("Asistente LADM-COL", message, level, duration=0) def xtf_browser_opened_to_true(self): """ Slot. Sets a flag to true to eventually avoid asking a user whether to overwrite a file. """ self.xtf_browser_was_opened = True def xtf_browser_opened_to_false(self): """ Slot. Sets a flag to false to eventually ask a user whether to overwrite a file. """ self.xtf_browser_was_opened = False self.clear_messages() def _connect_ili2db_signals(self, ili2db): ili2db.process_started.connect(self.on_process_started) ili2db.stderr.connect(self.on_stderr) ili2db.stdout.connect(self.print_info) ili2db.process_finished.connect(self.on_process_finished) def _disconnect_ili2db_signals(self, ili2db): ili2db.process_started.disconnect(self.on_process_started) ili2db.stderr.disconnect(self.on_stderr) ili2db.stdout.disconnect(self.print_info) ili2db.process_finished.disconnect(self.on_process_finished)
class DBManager(QMainWindow): def __init__(self, iface, parent=None): QMainWindow.__init__(self, parent) self.setAttribute(Qt.WA_DeleteOnClose) self.setupUi() self.iface = iface # restore the window state settings = QgsSettings() self.restoreGeometry(settings.value("/DB_Manager/mainWindow/geometry", QByteArray(), type=QByteArray)) self.restoreState(settings.value("/DB_Manager/mainWindow/windowState", QByteArray(), type=QByteArray)) self.tabs.currentChanged.connect(self.tabChanged) self.tree.selectedItemChanged.connect(self.itemChanged) self.itemChanged(None) def closeEvent(self, e): self.unregisterAllActions() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) # save the window state settings = QgsSettings() settings.setValue("/DB_Manager/mainWindow/windowState", self.saveState()) settings.setValue("/DB_Manager/mainWindow/geometry", self.saveGeometry()) QMainWindow.closeEvent(self, e) def refreshItem(self, item=None): with OverrideCursor(Qt.WaitCursor): try: if item is None: item = self.tree.currentItem() self.tree.refreshItem(item) # refresh item children in the db tree except BaseError as e: DlgDbError.showError(e, self) def itemChanged(self, item): with OverrideCursor(Qt.WaitCursor): try: self.reloadButtons() # clear preview, this will delete the layer in preview tab self.preview.loadPreview(None) self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def reloadButtons(self): db = self.tree.currentDatabase() if not hasattr(self, '_lastDb'): self._lastDb = db elif db == self._lastDb: return # remove old actions if self._lastDb is not None: self.unregisterAllActions() # add actions of the selected database self._lastDb = db if self._lastDb is not None: self._lastDb.registerAllActions(self) def tabChanged(self, index): with OverrideCursor(Qt.WaitCursor): try: self.refreshTabs() except BaseError as e: DlgDbError.showError(e, self) def refreshTabs(self): index = self.tabs.currentIndex() item = self.tree.currentItem() table = self.tree.currentTable() # enable/disable tabs self.tabs.setTabEnabled(self.tabs.indexOf(self.table), table is not None) self.tabs.setTabEnabled(self.tabs.indexOf(self.preview), table is not None and table.type in [table.VectorType, table.RasterType] and table.geomColumn is not None) # show the info tab if the current tab is disabled if not self.tabs.isTabEnabled(index): self.tabs.setCurrentWidget(self.info) current_tab = self.tabs.currentWidget() if current_tab == self.info: self.info.showInfo(item) elif current_tab == self.table: self.table.loadData(item) elif current_tab == self.preview: self.preview.loadPreview(item) def refreshActionSlot(self): self.info.setDirty() self.table.setDirty() self.preview.setDirty() self.refreshItem() def importActionSlot(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) return outUri = db.uri() schema = self.tree.currentSchema() if schema: outUri.setDataSource(schema.name, "", "", "") from .dlg_import_vector import DlgImportVector dlg = DlgImportVector(None, db, outUri, self) dlg.exec_() def exportActionSlot(self): table = self.tree.currentTable() if table is None: self.infoBar.pushMessage(self.tr("Select the table you want export to file."), QgsMessageBar.INFO, self.iface.messageTimeout()) return inLayer = table.toMapLayer() if inLayer.type() != QgsMapLayer.VectorLayer: self.infoBar.pushMessage( self.tr("Select a vector or a tabular layer you want export."), QgsMessageBar.WARNING, self.iface.messageTimeout()) return from .dlg_export_vector import DlgExportVector dlg = DlgExportVector(inLayer, table.database(), self) dlg.exec_() inLayer.deleteLater() def runSqlWindow(self): db = self.tree.currentDatabase() if db is None: self.infoBar.pushMessage(self.tr("No database selected or you are not connected to it."), QgsMessageBar.INFO, self.iface.messageTimeout()) # force displaying of the message, it appears on the first tab (i.e. Info) self.tabs.setCurrentIndex(0) return from .dlg_sql_window import DlgSqlWindow query = DlgSqlWindow(self.iface, db, self) dbname = db.connection().connectionName() tabname = self.tr("Query ({0})").format(dbname) index = self.tabs.addTab(query, tabname) self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) query.nameChanged.connect(functools.partial(self.update_query_tab_name, index, dbname)) def runSqlLayerWindow(self, layer): from .dlg_sql_layer_window import DlgSqlLayerWindow query = DlgSqlLayerWindow(self.iface, layer, self) lname = layer.name() tabname = self.tr("Layer ({0})").format(lname) index = self.tabs.addTab(query, tabname) # self.tabs.setTabIcon(index, db.connection().icon()) self.tabs.setCurrentIndex(index) def update_query_tab_name(self, index, dbname, queryname): if not queryname: queryname = self.tr("Query") tabname = u"%s (%s)" % (queryname, dbname) self.tabs.setTabText(index, tabname) def showSystemTables(self): self.tree.showSystemTables(self.actionShowSystemTables.isChecked()) def registerAction(self, action, menuName, callback=None): """ register an action to the manager's main menu """ if not hasattr(self, '_registeredDbActions'): self._registeredDbActions = {} if callback is not None: def invoke_callback(x): return self.invokeCallback(callback) if menuName is None or menuName == "": self.addAction(action) if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True # search for the menu actionMenu = None helpMenuAction = None for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue if a.menu() != self.menuHelp: helpMenuAction = a actionMenu = a break # not found, add a new menu before the help menu if actionMenu is None: menu = QMenu(menuName, self) if helpMenuAction is not None: actionMenu = self.menuBar.insertMenu(helpMenuAction, menu) else: actionMenu = self.menuBar.addMenu(menu) menu = actionMenu.menu() menuActions = menu.actions() # get the placeholder's position to insert before it pos = 0 for pos in range(len(menuActions)): if menuActions[pos].isSeparator() and menuActions[pos].objectName().endswith("_placeholder"): menuActions[pos].setVisible(True) break if pos < len(menuActions): before = menuActions[pos] menu.insertAction(before, action) else: menu.addAction(action) actionMenu.setVisible(True) # show the menu if menuName not in self._registeredDbActions: self._registeredDbActions[menuName] = list() self._registeredDbActions[menuName].append(action) if callback is not None: action.triggered.connect(invoke_callback) return True def invokeCallback(self, callback, *params): """ Call a method passing the selected item in the database tree, the sender (usually a QAction), the plugin mainWindow and optionally additional parameters. This method takes care to override and restore the cursor, but also catches exceptions and displays the error dialog. """ with OverrideCursor(Qt.WaitCursor): try: callback(self.tree.currentItem(), self.sender(), self, *params) except BaseError as e: # catch database errors and display the error dialog DlgDbError.showError(e, self) def unregisterAction(self, action, menuName): if not hasattr(self, '_registeredDbActions'): return if menuName is None or menuName == "": self.removeAction(action) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) action.deleteLater() return True for a in self.menuBar.actions(): if not a.menu() or a.menu().title() != menuName: continue menu = a.menu() menuActions = menu.actions() menu.removeAction(action) if menu.isEmpty(): # hide the menu a.setVisible(False) if menuName in self._registeredDbActions: if self._registeredDbActions[menuName].count(action) > 0: self._registeredDbActions[menuName].remove(action) # hide the placeholder if there're no other registered actions if len(self._registeredDbActions[menuName]) <= 0: for i in range(len(menuActions)): if menuActions[i].isSeparator() and menuActions[i].objectName().endswith("_placeholder"): menuActions[i].setVisible(False) break action.deleteLater() return True return False def unregisterAllActions(self): if not hasattr(self, '_registeredDbActions'): return for menuName in self._registeredDbActions: for action in list(self._registeredDbActions[menuName]): self.unregisterAction(action, menuName) del self._registeredDbActions def close_tab(self, index): widget = self.tabs.widget(index) if widget not in [self.info, self.table, self.preview]: self.tabs.removeTab(index) widget.deleteLater() def setupUi(self): self.setWindowTitle(self.tr("DB Manager")) self.setWindowIcon(QIcon(":/db_manager/icon")) self.resize(QSize(700, 500).expandedTo(self.minimumSizeHint())) # create central tab widget and add the first 3 tabs: info, table and preview self.tabs = QTabWidget() self.info = InfoViewer(self) self.tabs.addTab(self.info, self.tr("Info")) self.table = TableViewer(self) self.tabs.addTab(self.table, self.tr("Table")) self.preview = LayerPreview(self) self.tabs.addTab(self.preview, self.tr("Preview")) self.setCentralWidget(self.tabs) # display close button for all tabs but the first 3 ones, i.e. # HACK: just hide the close button where not needed (GS) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) tabbar = self.tabs.tabBar() for i in range(3): btn = tabbar.tabButton(i, QTabBar.RightSide) if tabbar.tabButton(i, QTabBar.RightSide) else tabbar.tabButton(i, QTabBar.LeftSide) btn.resize(0, 0) btn.hide() # Creates layout for message bar self.layout = QGridLayout(self.info) self.layout.setContentsMargins(0, 0, 0, 0) spacerItem = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) self.layout.addItem(spacerItem, 1, 0, 1, 1) # init messageBar instance self.infoBar = QgsMessageBar(self.info) sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.infoBar.setSizePolicy(sizePolicy) self.layout.addWidget(self.infoBar, 0, 0, 1, 1) # create database tree self.dock = QDockWidget("Tree", self) self.dock.setObjectName("DB_Manager_DBView") self.dock.setFeatures(QDockWidget.DockWidgetMovable) self.tree = DBTree(self) self.dock.setWidget(self.tree) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock) # create status bar self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) # create menus self.menuBar = QMenuBar(self) self.menuDb = QMenu(self.tr("&Database"), self) self.menuBar.addMenu(self.menuDb) self.menuSchema = QMenu(self.tr("&Schema"), self) actionMenuSchema = self.menuBar.addMenu(self.menuSchema) self.menuTable = QMenu(self.tr("&Table"), self) actionMenuTable = self.menuBar.addMenu(self.menuTable) self.menuHelp = None # QMenu(self.tr("&Help"), self) # actionMenuHelp = self.menuBar.addMenu(self.menuHelp) self.setMenuBar(self.menuBar) # create toolbar self.toolBar = QToolBar("Default", self) self.toolBar.setObjectName("DB_Manager_ToolBar") self.addToolBar(self.toolBar) # create menus' actions # menu DATABASE sep = self.menuDb.addSeparator() sep.setObjectName("DB_Manager_DbMenu_placeholder") sep.setVisible(False) self.actionRefresh = self.menuDb.addAction(QIcon(":/db_manager/actions/refresh"), self.tr("&Refresh"), self.refreshActionSlot, QKeySequence("F5")) self.actionSqlWindow = self.menuDb.addAction(QIcon(":/db_manager/actions/sql_window"), self.tr("&SQL window"), self.runSqlWindow, QKeySequence("F2")) self.menuDb.addSeparator() self.actionClose = self.menuDb.addAction(QIcon(), self.tr("&Exit"), self.close, QKeySequence("CTRL+Q")) # menu SCHEMA sep = self.menuSchema.addSeparator() sep.setObjectName("DB_Manager_SchemaMenu_placeholder") sep.setVisible(False) actionMenuSchema.setVisible(False) # menu TABLE sep = self.menuTable.addSeparator() sep.setObjectName("DB_Manager_TableMenu_placeholder") sep.setVisible(False) self.actionImport = self.menuTable.addAction(QIcon(":/db_manager/actions/import"), self.tr("&Import layer/file"), self.importActionSlot) self.actionExport = self.menuTable.addAction(QIcon(":/db_manager/actions/export"), self.tr("&Export to file"), self.exportActionSlot) self.menuTable.addSeparator() #self.actionShowSystemTables = self.menuTable.addAction(self.tr("Show system tables/views"), self.showSystemTables) #self.actionShowSystemTables.setCheckable(True) #self.actionShowSystemTables.setChecked(True) actionMenuTable.setVisible(False) # add actions to the toolbar self.toolBar.addAction(self.actionRefresh) self.toolBar.addAction(self.actionSqlWindow) self.toolBar.addAction(self.actionImport) self.toolBar.addAction(self.actionExport)