def __init__(self): QtWidgets.QDialog.__init__(self) self.setMinimumWidth(600) self.setMinimumHeight(450) self.helpFile = os.path.join(os.path.dirname(__file__),'README.html') self.setWindowTitle('VectorBender') txt = QTextBrowser() txt.setReadOnly(True) txt.setSearchPaths([os.path.dirname(__file__)]) txt.setOpenExternalLinks(True) txt.setText( open(self.helpFile, 'r').read() ) cls = QPushButton('Close') cls.pressed.connect(self.accept) lay = QVBoxLayout() lay.addWidget(txt) lay.addWidget(cls) self.setLayout(lay)
def __init__(self): QWidget.__init__(self) self.setWindowTitle(self.tr('Qdraw - Settings')) self.setFixedSize(320, 100) self.center() # default color self.color = QColor(60, 151, 255, 255) self.sld_opacity = QSlider(Qt.Horizontal, self) self.sld_opacity.setRange(0, 255) self.sld_opacity.setValue(255) self.sld_opacity.tracking = True self.sld_opacity.valueChanged.connect(self.handler_opacitySliderValue) self.lbl_opacity = QLabel(self.tr('Opacity') + ': 100%', self) self.dlg_color = QColorDialog(self) btn_chColor = QPushButton(self.tr('Change the drawing color'), self) btn_chColor.clicked.connect(self.handler_chColor) vbox = QVBoxLayout() vbox.addWidget(self.lbl_opacity) vbox.addWidget(self.sld_opacity) vbox.addWidget(btn_chColor) self.setLayout(vbox)
class ProgressBarLogger(QDialog): """A simple dialog with a progress bar and a label""" def __init__(self, title = None): QDialog.__init__(self, None) if title is not None: self.setWindowTitle(title) self.__label = QLabel(self) self.__layout = QVBoxLayout() self.__layout.addWidget(self.__label) self.__progress = QProgressBar(self) self.__layout.addWidget(self.__progress) self.setLayout(self.__layout) self.resize(600, 70) self.setFixedSize(600, 70) self.__progress.hide() self.show() def set_text(self, t): """Gets called when a text is to be logged""" if isinstance(t, tuple): lvl, msg = t else: msg = t self.__label.setText(msg) QCoreApplication.processEvents() def set_progress(self, i, n): """Gets called when there is a progression""" self.__progress.show() self.__progress.setMinimum(0) self.__progress.setMaximum(n) self.__progress.setValue(i) QCoreApplication.processEvents()
def initGui(self): hlayout = QHBoxLayout() layout = QVBoxLayout() self.searchBox = QLineEdit() self.searchBox.returnPressed.connect(self.search) self.searchBox.setPlaceholderText("[Enter search string and press enter to search for maps]") hlayout.addWidget(self.searchBox) self.button = QToolButton() self.button.setText("Search") self.button.clicked.connect(self.search) self.button.adjustSize() self.searchBox.setFixedHeight(self.button.height()) hlayout.addWidget(self.button) layout.addLayout(hlayout) w = QFrame() self.browser = QWebView() w.setStyleSheet("QFrame{border:1px solid rgb(0, 0, 0);}") innerlayout = QHBoxLayout() innerlayout.setSpacing(0) innerlayout.setMargin(0) innerlayout.addWidget(self.browser) w.setLayout(innerlayout) layout.addWidget(w) self.setLayout(layout) self.browser.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.browser.settings().setUserStyleSheetUrl(QUrl("file://" + resourceFile("search.css").replace("\\", "/"))) self.browser.linkClicked.connect(self.linkClicked) self.resize(600, 500) self.setWindowTitle("Search stories")
def setupUi(self): self.resize(800, 600) self.setWindowTitle("Authorship") layout = QVBoxLayout() splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) self.table = QTableWidget(splitter) self.table.setColumnCount(3) self.table.setShowGrid(False) self.table.verticalHeader().hide() self.table.setHorizontalHeaderLabels(["Attribute", "Author", "Value"]) self.table.setRowCount(len(self.blamedata)) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setSelectionMode(QAbstractItemView.SingleSelection); self.table.selectionModel().selectionChanged.connect(self.selectionChanged) for i, name in enumerate(self.blamedata.keys()): values = self.blamedata[name] self.table.setItem(i, 0, QTableWidgetItem(name)); self.table.setItem(i, 1, QTableWidgetItem(values[1].authorname)); self.table.setItem(i, 2, QTableWidgetItem(values[0])); self.table.resizeRowsToContents() self.table.horizontalHeader().setMinimumSectionSize(250) self.table.horizontalHeader().setStretchLastSection(True) self.text = QTextBrowser(splitter) layout.addWidget(splitter) self.setLayout(layout) QMetaObject.connectSlotsByName(self)
class LessonFinishedDialog(QDialog): def __init__(self, lesson): super(LessonFinishedDialog, self).__init__(iface.mainWindow()) self.reopen = False self.lesson = lesson self.setWindowTitle("Lesson finished") self.verticalLayout = QVBoxLayout() self.verticalLayout.setSpacing(10) self.verticalLayout.setMargin(10) txt = "<p>Congratulations! You have correctly finished this lesson.</p>" if lesson.nextLessons: txt += "<p>We recommend you the following lessons to continue:</p><ul>" for i, nextLesson in enumerate(lesson.nextLessons): txt+="<li><a href='%i'>%s</a>" % (i, nextLesson[1]) txt += "</ul><p>If you don't want to run more lessons, just <a href='exit'>close this dialog.</a></p>" txt += "<p>If you want to run another lesson, click <a href='reopen'>here</a> to reopen the lesson selector</p>" self.text = QTextBrowser() self.text.anchorClicked.connect(self.linkClicked) self.text.setHtml(txt) self.text.setOpenLinks(False) self.verticalLayout.addWidget(self.text) self.setLayout(self.verticalLayout) self.resize(400, 300) self.nextLesson = None def linkClicked(self, url): if url.path() not in ["exit", "reopen"]: self.nextLesson = lessonFromName(*self.lesson.nextLessons[int(url.path())]) if url.path() =="reopen": self.reopen = True self.close()
def initGui(self): layout = QVBoxLayout() self.tab = QTabWidget() self.table = QTableView() self.setLayout(layout) self.canvas = QgsMapCanvas() self.canvas.setCanvasColor(Qt.white) settings = QSettings() self.canvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type = bool)) self.canvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type = bool)) self.canvas.mapSettings().setDestinationCrs(self.crs) action = settings.value("/qgis/wheel_action", 0, type = float) zoomFactor = settings.value("/qgis/zoom_factor", 2, type = float) self.canvas.setWheelAction(QgsMapCanvas.WheelAction(action), zoomFactor) self.panTool = QgsMapToolPan(self.canvas) self.canvas.setMapTool(self.panTool) execute(self.createLayers) model = GeomDiffTableModel(self.data) self.table.setModel(model) self.table.resizeColumnsToContents() self.table.resizeRowsToContents() self.tab.addTab(self.canvas, "Map view") self.tab.addTab(self.table, "Table view") layout.addWidget(self.tab) self.resize(600, 500) self.setWindowTitle("Geometry comparison")
def __init__(self, layer, iface): super().__init__(None) self.setWindowModality(Qt.WindowModal) layout = QVBoxLayout() layout.setMargin(0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) dlgContext = QgsAttributeEditorContext() dlgContext.setFormMode(QgsAttributeEditorContext.StandaloneDialog) dlgContext.setAllowCustomUi(False) self.form = QgsAttributeForm(layer, context=dlgContext, parent=self) self.form.setMode(3) self.configureForm() layout.addWidget(self.form) self.form.zoomToFeatures.connect(self.zoomToFeatures) self.form.closed.connect(self.close) self.setWindowTitle(self.tr('Filter NavTable Features by Form')) self.expression = ''
def __init__(self, parent, settings): super(ConfigOptionsPage, self).__init__(parent) self.settings = settings self.config_widget = ConfigDialog(self.settings) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setMargin(0) self.setLayout(layout) layout.addWidget(self.config_widget) self.setObjectName('geosearchOptions')
def widget_dialog(self, widget): dlg = QDialog() widget.setParent(dlg) layout = QVBoxLayout() layout.addWidget(widget) layout.setMargin(6) button_box = QDialogButtonBox(QDialogButtonBox.Close) button_box.rejected.connect(dlg.close) layout.addWidget(button_box) dlg.setLayout(layout) return dlg
def __init__(self, parent, *args, **kwargs): QDialog.__init__(self, parent, Qt.Window) self.setWindowTitle("Plot dialog") layout = QVBoxLayout(self) self.plot = self.createPlot(*args, **kwargs) layout.addWidget(self.plot) self.nav = self.createToolBar() layout.addWidget(self.nav)
class QMatplotlibWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.canvas = QMatplotlibCanvas() self.ax = self.canvas.ax self.figure = self.canvas.figure self.toolBar = NavigationToolbar2QT(self.canvas, self) bgColor = self.palette().color(QPalette.Background).name() self.figure.set_facecolor(bgColor) self.layout = QVBoxLayout() self.layout.setSpacing(2) self.layout.setMargin(0) self.layout.addWidget(self.toolBar) self.layout.addWidget(self.canvas) self.setLayout(self.layout) self._setupToolbar() def _setupToolbar(self): self.actionToggleGrid = QAction(self.tr('Toggle grid'), self.toolBar) self.actionToggleGrid.setIcon( QIcon(os.path.join(pluginPath, 'icons', 'toggleGrid.svg'))) self.actionToggleGrid.setCheckable(True) self.actionToggleGrid.triggered.connect(self.toggleGrid) self.toolBar.insertAction(self.toolBar.actions()[7], self.actionToggleGrid) self.toolBar.insertSeparator(self.toolBar.actions()[8]) def toggleGrid(self): self.ax.grid() self.canvas.draw() def alignLabels(self): self.figure.autofmt_xdate() def clear(self): self.ax.clear() self.canvas.draw() def setTitle(self, text): self.ax.set_title(text) def setXAxisCaption(self, text): self.ax.set_xlabel(text) def setYAxisCaption(self, text): self.ax.set_ylabel(text)
def __init__(self, s): QDialog.__init__(self) self.setModal(True) self.resize(600, 400) self.setWindowTitle(self.tr('Unit test')) layout = QVBoxLayout() self.text = QTextEdit() self.text.setFontFamily("monospace") self.text.setEnabled(True) self.text.setText(s) layout.addWidget(self.text) self.setLayout(layout) QMetaObject.connectSlotsByName(self)
def initGui(self): layout = QVBoxLayout() self.history = HistoryViewer(False) self.history.updateContent(self.repo, self.layer) layout.addWidget(self.history) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Close) buttonBox.accepted.connect(self.okPressed) buttonBox.rejected.connect(self.cancelPressed) layout.addWidget(buttonBox) self.setLayout(layout) self.resize(400, 500) self.setWindowTitle("Repository history")
def __init__(self): super(NavigatorDialog, self).__init__(None) self.currentRepo = None self.reposItem = None self.setupUi(self) self.repoTree.setContextMenuPolicy(Qt.CustomContextMenu) self.repoTree.setSelectionBehavior(QAbstractItemView.SelectRows) self.repoTree.itemSelectionChanged.connect(self.selectionChanged) self.repoTree.customContextMenuRequested.connect(self.showPopupMenu) self.comboEndpoint.currentIndexChanged.connect(self.fillTree) self.btnAddServer.setIcon(icon("add-server.svg")) self.btnEditServer.setIcon(icon("edit-server.svg")) self.btnDeleteServer.setIcon(icon("delete-server.svg")) self.btnAddRepo.setIcon(icon("add-repository.svg")) self.btnRefresh.setIcon(icon("refresh.svg")) self.btnAddServer.clicked.connect(self.addGeoGigServer) self.btnEditServer.clicked.connect(self.editGeoGigServer) self.btnDeleteServer.clicked.connect(self.deleteGeoGigServer) self.btnAddRepo.clicked.connect(self.createRepo) self.btnRefresh.clicked.connect(self.fillTree) self.btnRefresh.clicked.connect(self.refreshTree) self._enableOrDisableButtons() if qtVersion < 5: self.repoTree.header().setResizeMode(0, QHeaderView.Stretch) self.repoTree.header().setResizeMode(1, QHeaderView.ResizeToContents) self.versionsTree = HistoryViewer() layout = QVBoxLayout() layout.addWidget(QLabel("Repository history")) layout.addWidget(self.versionsTree) self.versionsWidget.setLayout(layout) def _repoChanged(repo): if self.currentRepo is not None and repo.url == self.currentRepo.url: self.updateCurrentRepo(repo, True) for i in range(self.repoTree.topLevelItemCount()): item = self.repoTree.topLevelItem(i) if item.repo == repo: item.refreshContent() repoWatcher.repoChanged.connect(_repoChanged) self.updateNavigator() self.repoTree.itemExpanded.connect(self._itemExpanded)
def __init__(self, title, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(title) layout = QVBoxLayout() self.selector = QgsProjectionSelector(self) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Close) layout.addWidget(self.selector) layout.addWidget(buttonBox) self.setLayout(layout) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject)
def __init__(self, s): QDialog.__init__(self) self.setModal(True) self.resize(600, 400) self.setWindowTitle(self.tr('Unit test')) layout = QVBoxLayout() self.text = QTextEdit() self.text.setFontFamily("monospace") self.text.setEnabled(True) # Add two spaces in front of each text for faster copy/paste self.text.setText(' {}'.format(s.replace('\n', '\n '))) layout.addWidget(self.text) self.setLayout(layout) QMetaObject.connectSlotsByName(self)
def _setupUI(self): self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # unselected widget self.unselected_widget = QListWidget(self) self._set_list_widget_defaults(self.unselected_widget) unselected_label = QLabel() unselected_label.setText("Unselected") unselected_label.setAlignment(Qt.AlignCenter) unselected_label.setFont(italic_font) unselected_v_layout = QVBoxLayout() unselected_v_layout.addWidget(unselected_label) unselected_v_layout.addWidget(self.unselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText("Selected") selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton(">>") self.deselect_all_btn = SmallQPushButton("<<") self.select_btn = SmallQPushButton(">") self.deselect_btn = SmallQPushButton("<") self.select_btn.setToolTip("Add the selected items") self.deselect_btn.setToolTip("Remove the selected items") self.select_all_btn.setToolTip("Add all") self.deselect_all_btn.setToolTip("Remove all") # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(unselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout)
class LoadWizardGMLAS(QWizardPage): def __init__(self, parent): super().__init__(parent) self._panel = ImportGmlasPanel(self) self._layout = QVBoxLayout() self._layout.addWidget(self._panel) self.setLayout(self._layout) self.setTitle("GMLAS Options") def gml_path(self): return self.wizard().gml_path() def validatePage(self): self._panel.do_load() return True
def editAuthCfgId(self): dlg = QDialog(self) dlg.setWindowModality(Qt.WindowModal) layout = QVBoxLayout() selector = QgsAuthConfigSelect(self) if self.editAuthCfg.text(): selector.setConfigId(self.editAuthCfg.text()) layout.addWidget(selector) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Close) buttonBox.accepted.connect(dlg.accept) buttonBox.rejected.connect(dlg.reject) layout.addWidget(buttonBox) dlg.setLayout(layout) if dlg.exec_(): self.editAuthCfg.setText(selector.configId()) del dlg
def __init__(self, plugin, html, gmlid, parent): QDialog.__init__(self, parent) self.resize(QSize(740, 580)) self.setWindowTitle(u"Flurstücksnachweis") self.plugin = plugin self.gmlid = gmlid self.tbEigentuemer = QTextBrowser(self) self.tbEigentuemer.setHtml(html) self.tbEigentuemer.setContextMenuPolicy(Qt.NoContextMenu) layout = QVBoxLayout(self) layout.addWidget(self.tbEigentuemer) self.setLayout(layout) self.restoreGeometry(QSettings("norBIT", "norGIS-ALKIS-Erweiterung").value("infogeom", QByteArray(), type=QByteArray)) self.move(self.pos() + QPoint(16, 16) * len(self.info))
def __init__(self, parent, alg): ParametersPanel.__init__(self, parent, alg) w = QWidget() layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(6) label = QLabel() label.setText(self.tr("GDAL/OGR console call")) layout.addWidget(label) self.text = QPlainTextEdit() self.text.setReadOnly(True) layout.addWidget(self.text) w.setLayout(layout) self.layoutMain.addWidget(w) self.connectParameterSignals() self.parametersHaveChanged()
def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg 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()
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.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 initGui(self): self.setWindowTitle('Remote connection reference') verticalLayout = QVBoxLayout() horizontalLayout = QHBoxLayout() horizontalLayout.setSpacing(30) horizontalLayout.setMargin(0) remoteLabel = QLabel('Remote connection') self.remoteCombo = QComboBox() self.remotes = self.repo.remotes() self.remoteCombo.addItems(list(self.remotes.keys())) self.remoteCombo.currentIndexChanged.connect(self.currentRemoteChanged) horizontalLayout.addWidget(remoteLabel) horizontalLayout.addWidget(self.remoteCombo) verticalLayout.addLayout(horizontalLayout) horizontalLayout = QHBoxLayout() horizontalLayout.setSpacing(30) horizontalLayout.setMargin(0) branchLabel = QLabel('Branch') self.branchCombo = QComboBox() self.branchCombo.addItems(self.repo.branches()) horizontalLayout.addWidget(branchLabel) horizontalLayout.addWidget(self.branchCombo) verticalLayout.addLayout(horizontalLayout) self.groupBox = QGroupBox() self.groupBox.setTitle("Remote connection info") self.groupBox.setLayout(verticalLayout) layout = QVBoxLayout() layout.addWidget(self.groupBox) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) layout.addWidget(self.buttonBox) self.setLayout(layout) self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.resize(400, 200)
class MultilineTextPanel(QWidget): USE_TEXT = 0 def __init__(self, options, parent=None): super(MultilineTextPanel, self).__init__(parent) self.options = options self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setSpacing(2) self.verticalLayout.setMargin(0) self.combo = QComboBox() self.combo.addItem(self.tr('[Use text below]')) for option in options: self.combo.addItem(option[0], option[1]) self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout.addWidget(self.combo) self.textBox = QPlainTextEdit() self.verticalLayout.addWidget(self.textBox) self.setLayout(self.verticalLayout) def setText(self, text): self.textBox.setPlainText(text) def getOption(self): return self.combo.currentIndex() def getValue(self): if self.combo.currentIndex() == 0: return str(self.textBox.toPlainText()) else: return self.combo.currentData() def setValue(self, value): items = [self.combo.itemData(i) for i in range(1, self.combo.count())] for idx, item in enumerate(items): if item == value: self.combo.setCurrentIndex(idx) return self.combo.setCurrentIndex(0) if value: self.textBox.setPlainText(value)
def __init__(self): QWidget.__init__(self) layout = QVBoxLayout() labelName = QLabel("Name") labelGroup = QLabel("Group") self.txtName = QLineEdit() self.txtGroup = QLineEdit() layout.addWidget(labelName) layout.addWidget(self.txtName) layout.addWidget(labelGroup) layout.addWidget(self.txtGroup) layout.addStretch() self.setLayout(layout)
class DialogWithImage(QDialog): def __init__(self, interface): QDialog.__init__(self, interface.mainWindow()) self.iface = interface self.main_widget = QWidget(self) self.main_widget.setMinimumSize(QSize(100, 100)) self.label = QLabel() self.buttonBox = QDialogButtonBox(self.main_widget) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok) self.buttonBox.accepted.connect(self.Apply) # Access the layout of the MessageBox to add the checkbox self.container = QVBoxLayout(self.main_widget) self.container.addWidget(self.label) self.container.addWidget(self.buttonBox) self.container.setAlignment(Qt.AlignCenter) self.container.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(self.container) def Apply(self): self.close()
def __init__(self, alg): AlgorithmDialogBase.__init__(self, alg) self.alg = alg self.mainWidget = ParametersPanel(self, alg) self.setMainWidget() 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) QgsMapLayerRegistry.instance().layerWasAdded.connect(self.mainWidget.layerAdded) QgsMapLayerRegistry.instance().layersWillBeRemoved.connect(self.mainWidget.layersWillBeRemoved)
def on_copy(self): select_group_dialog = QDialog(self) select_group_dialog.resize(300, 400) select_group_dialog.setWindowTitle(self.tr("Choose source group")) layout = QVBoxLayout(select_group_dialog) select_group_dialog.setLayout(layout) groups_list_view = QTableView(self) layout.addWidget(groups_list_view) groups_list_view.setModel(self.ds_model) groups_list_view.setColumnHidden(DSManagerModel.COLUMN_VISIBILITY, True) groups_list_view.setSelectionMode(QTableView.NoSelection) groups_list_view.setAlternatingRowColors(True) groups_list_view.setShowGrid(False) if hasattr(groups_list_view.horizontalHeader(), "setResizeMode"): # Qt4 groups_list_view.horizontalHeader().setResizeMode(DSManagerModel.COLUMN_GROUP_DS, QHeaderView.Stretch) groups_list_view.verticalHeader().setResizeMode(QHeaderView.ResizeToContents) else: # Qt5 groups_list_view.horizontalHeader().setSectionResizeMode(DSManagerModel.COLUMN_GROUP_DS, QHeaderView.Stretch) groups_list_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) groups_list_view.verticalHeader().hide() groups_list_view.clicked.connect( lambda index: select_group_dialog.accept() \ if self.ds_model.isGroup(index) and \ index.column() == DSManagerModel.COLUMN_GROUP_DS \ else None ) if select_group_dialog.exec_() == QDialog.Accepted: group_info = self.ds_model.data(groups_list_view.currentIndex(), Qt.UserRole) group_info.id += "_copy" edit_dialog = GroupEditDialog() edit_dialog.setWindowTitle(self.tr('Create group from existing')) edit_dialog.fill_group_info(group_info) if edit_dialog.exec_() == QDialog.Accepted: self.feel_list() self.ds_model.resetModel()
class ModelerParameterDefinitionDialog(QDialog): @staticmethod def use_legacy_dialog(param=None, paramType=None): if paramType in (parameters.PARAMETER_TABLE_FIELD, parameters.PARAMETER_BAND, parameters.PARAMETER_VECTOR, parameters.PARAMETER_TABLE, parameters.PARAMETER_MULTIPLE, parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE, parameters.PARAMETER_ENUM, parameters.PARAMETER_MATRIX, parameters.PARAMETER_MAP_LAYER): return True elif isinstance(param, (QgsProcessingParameterField, QgsProcessingParameterBand, QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers, QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale, QgsProcessingParameterEnum, QgsProcessingParameterMatrix, QgsProcessingParameterMapLayer, QgsProcessingDestinationParameter)): return True # yay, use new API! return False def __init__(self, alg, paramType=None, param=None): self.alg = alg self.paramType = paramType self.param = param QDialog.__init__(self) self.setModal(True) self.setupUi() settings = QgsSettings() self.restoreGeometry(settings.value("/Processing/modelParametersDefinitionDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) super(ModelerParameterDefinitionDialog, self).closeEvent(event) def switchToCommentTab(self): self.tab.setCurrentIndex(1) self.commentEdit.setFocus() self.commentEdit.selectAll() def setupUi(self): type_metadata = QgsApplication.processingRegistry().parameterType(self.param.type() if self.param else self.paramType) self.setWindowTitle(self.tr('{} Parameter Definition').format(type_metadata.name())) self.mainLayout = QVBoxLayout() self.tab = QTabWidget() self.mainLayout.addWidget(self.tab) self.setMinimumWidth(300) self.verticalLayout = QVBoxLayout() self.label = QLabel(self.tr('Parameter name')) self.verticalLayout.addWidget(self.label) self.nameTextBox = QLineEdit() self.verticalLayout.addWidget(self.nameTextBox) if isinstance(self.param, QgsProcessingParameterDefinition): self.nameTextBox.setText(self.param.description()) if self.paramType == parameters.PARAMETER_TABLE_FIELD or \ isinstance(self.param, QgsProcessingParameterField): self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() idx = 0 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition(param.parameterName()) if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentLayerParameterName() == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) # add the datatype selector self.verticalLayout.addWidget(QLabel(self.tr('Allowed data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any'), -1) self.datatypeCombo.addItem(self.tr('Number'), 0) self.datatypeCombo.addItem(self.tr('String'), 1) self.datatypeCombo.addItem(self.tr('Date/time'), 2) self.verticalLayout.addWidget(self.datatypeCombo) if self.param is not None and self.param.dataType() is not None: # QComboBoxes indexes start at 0, # self.param.datatype start with -1 that is why I need to do +1 datatypeIndex = self.param.dataType() + 1 self.datatypeCombo.setCurrentIndex(datatypeIndex) self.multipleCheck = QCheckBox() self.multipleCheck.setText(self.tr('Accept multiple fields')) self.multipleCheck.setChecked(False) if self.param is not None: self.multipleCheck.setChecked(self.param.allowMultiple()) self.verticalLayout.addWidget(self.multipleCheck) self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() self.defaultTextBox.setToolTip( self.tr('Default field name, or ; separated list of field names for multiple field parameters')) if self.param is not None: default = self.param.defaultValue() if default is not None: self.defaultTextBox.setText(str(default)) self.verticalLayout.addWidget(self.defaultTextBox) elif self.paramType == parameters.PARAMETER_BAND or \ isinstance(self.param, QgsProcessingParameterBand): self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() idx = 0 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition(param.parameterName()) if isinstance(definition, (QgsProcessingParameterRasterLayer)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentLayerParameterName() == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) elif (self.paramType in ( parameters.PARAMETER_VECTOR, parameters.PARAMETER_TABLE) or isinstance(self.param, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer))): self.verticalLayout.addWidget(QLabel(self.tr('Geometry type'))) self.shapetypeCombo = QComboBox() self.shapetypeCombo.addItem(self.tr('Geometry Not Required'), QgsProcessing.TypeVector) self.shapetypeCombo.addItem(self.tr('Point'), QgsProcessing.TypeVectorPoint) self.shapetypeCombo.addItem(self.tr('Line'), QgsProcessing.TypeVectorLine) self.shapetypeCombo.addItem(self.tr('Polygon'), QgsProcessing.TypeVectorPolygon) self.shapetypeCombo.addItem(self.tr('Any Geometry Type'), QgsProcessing.TypeVectorAnyGeometry) if self.param is not None: self.shapetypeCombo.setCurrentIndex(self.shapetypeCombo.findData(self.param.dataTypes()[0])) self.verticalLayout.addWidget(self.shapetypeCombo) elif (self.paramType == parameters.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.verticalLayout.addWidget(QLabel(self.tr('Data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer) self.datatypeCombo.addItem(self.tr('Vector (No Geometry Required)'), QgsProcessing.TypeVector) self.datatypeCombo.addItem(self.tr('Vector (Point)'), QgsProcessing.TypeVectorPoint) self.datatypeCombo.addItem(self.tr('Vector (Line)'), QgsProcessing.TypeVectorLine) self.datatypeCombo.addItem(self.tr('Vector (Polygon)'), QgsProcessing.TypeVectorPolygon) self.datatypeCombo.addItem(self.tr('Vector (Any Geometry Type)'), QgsProcessing.TypeVectorAnyGeometry) self.datatypeCombo.addItem(self.tr('Raster'), QgsProcessing.TypeRaster) self.datatypeCombo.addItem(self.tr('File'), QgsProcessing.TypeFile) if self.param is not None: self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.layerType())) self.verticalLayout.addWidget(self.datatypeCombo) elif (self.paramType == parameters.PARAMETER_MAP_LAYER or isinstance(self.param, QgsProcessingParameterMapLayer)): self.verticalLayout.addWidget(QLabel(self.tr('Data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer) self.datatypeCombo.addItem(self.tr('Vector (Point)'), QgsProcessing.TypeVectorPoint) self.datatypeCombo.addItem(self.tr('Vector (Line)'), QgsProcessing.TypeVectorLine) self.datatypeCombo.addItem(self.tr('Vector (Polygon)'), QgsProcessing.TypeVectorPolygon) self.datatypeCombo.addItem(self.tr('Vector (Any Geometry Type)'), QgsProcessing.TypeVectorAnyGeometry) self.datatypeCombo.addItem(self.tr('Raster'), QgsProcessing.TypeRaster) self.datatypeCombo.addItem(self.tr('Mesh'), QgsProcessing.TypeMesh) if self.param is not None: self.datatypeCombo.setCurrentIndex(self.datatypeCombo.findData(self.param.dataTypes()[0])) self.verticalLayout.addWidget(self.datatypeCombo) elif (self.paramType in (parameters.PARAMETER_NUMBER, parameters.PARAMETER_DISTANCE, parameters.PARAMETER_SCALE) or isinstance(self.param, (QgsProcessingParameterNumber, QgsProcessingParameterDistance, QgsProcessingParameterScale))): if (self.paramType == parameters.PARAMETER_DISTANCE or isinstance(self.param, QgsProcessingParameterDistance)): self.verticalLayout.addWidget(QLabel(self.tr('Linked input'))) self.parentCombo = QComboBox() self.parentCombo.addItem('', '') idx = 1 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition(param.parameterName()) if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMapLayer, QgsProcessingParameterCrs)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentParameterName() == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) elif (self.paramType != parameters.PARAMETER_SCALE and not isinstance(self.param, QgsProcessingParameterScale)): self.verticalLayout.addWidget(QLabel(self.tr('Number type'))) self.type_combo = QComboBox() self.type_combo.addItem(self.tr('Float'), QgsProcessingParameterNumber.Double) self.type_combo.addItem(self.tr('Integer'), QgsProcessingParameterNumber.Integer) if self.param: self.type_combo.setCurrentIndex(self.type_combo.findData(self.param.dataType())) self.verticalLayout.addWidget(self.type_combo) if (self.paramType != parameters.PARAMETER_SCALE and not isinstance(self.param, QgsProcessingParameterScale)): self.verticalLayout.addWidget(QLabel(self.tr('Min value'))) self.minTextBox = QLineEdit() self.verticalLayout.addWidget(self.minTextBox) self.verticalLayout.addWidget(QLabel(self.tr('Max value'))) self.maxTextBox = QLineEdit() self.verticalLayout.addWidget(self.maxTextBox) if self.param is not None: self.minTextBox.setText(str(self.param.minimum())) self.maxTextBox.setText(str(self.param.maximum())) self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() self.defaultTextBox.setText(self.tr('0')) if self.param is not None: default = self.param.defaultValue() if self.param.dataType() == QgsProcessingParameterNumber.Integer: default = int(math.floor(float(default))) if default: self.defaultTextBox.setText(str(default)) self.verticalLayout.addWidget(self.defaultTextBox) elif self.paramType == parameters.PARAMETER_ENUM or \ isinstance(self.param, QgsProcessingParameterEnum): self.widget = EnumModelerWidget(self) if self.param is not None: self.widget.setAllowMultiple(bool(self.param.allowMultiple())) self.widget.setOptions(self.param.options()) self.widget.setDefault(self.param.defaultValue()) self.verticalLayout.addWidget(self.widget) elif self.paramType == parameters.PARAMETER_MATRIX or \ isinstance(self.param, QgsProcessingParameterMatrix): self.widget = MatrixModelerWidget(self) if self.param is not None: self.widget.setValue(self.param.headers(), self.param.defaultValue()) self.widget.setFixedRows(self.param.hasFixedNumberRows()) self.verticalLayout.addWidget(self.widget) elif isinstance(self.param, QgsProcessingDestinationParameter): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultWidget = QgsProcessingLayerOutputDestinationWidget(self.param, defaultSelection=True) self.verticalLayout.addWidget(self.defaultWidget) self.verticalLayout.addSpacing(20) self.requiredCheck = QCheckBox() self.requiredCheck.setText(self.tr('Mandatory')) self.requiredCheck.setChecked(True) if self.param is not None: self.requiredCheck.setChecked(not self.param.flags() & QgsProcessingParameterDefinition.FlagOptional) self.verticalLayout.addWidget(self.requiredCheck) self.advancedCheck = QCheckBox() self.advancedCheck.setText(self.tr('Advanced')) self.advancedCheck.setChecked(False) if self.param is not None: self.advancedCheck.setChecked(self.param.flags() & QgsProcessingParameterDefinition.FlagAdvanced) self.verticalLayout.addWidget(self.advancedCheck) # If child algorithm output is mandatory, disable checkbox if isinstance(self.param, QgsProcessingDestinationParameter): provider_name, child_name, output_name = self.param.name().split(':') child = self.alg.childAlgorithms()['{}:{}'.format(provider_name, child_name)] model_output = child.modelOutput(output_name) param_def = child.algorithm().parameterDefinition(model_output.childOutputName()) if not (param_def.flags() & QgsProcessingParameterDefinition.FlagOptional): self.requiredCheck.setEnabled(False) self.requiredCheck.setChecked(True) self.advancedCheck.setEnabled(False) self.advancedCheck.setChecked(False) self.verticalLayout.addStretch() w = QWidget() w.setLayout(self.verticalLayout) self.tab.addTab(w, self.tr('Properties')) self.commentLayout = QVBoxLayout() self.commentEdit = QTextEdit() self.commentEdit.setAcceptRichText(False) self.commentLayout.addWidget(self.commentEdit) w2 = QWidget() w2.setLayout(self.commentLayout) self.tab.addTab(w2, self.tr('Comments')) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.setObjectName('buttonBox') self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.mainLayout.addWidget(self.buttonBox) self.setLayout(self.mainLayout) def setComments(self, text): self.commentEdit.setPlainText(text) def comments(self): return self.commentEdit.toPlainText() def accept(self): description = self.nameTextBox.text() if description.strip() == '': QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Invalid parameter name')) return if self.param is None: validChars = \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' safeName = ''.join(c for c in description if c in validChars) name = safeName.lower() i = 2 while self.alg.parameterDefinition(name): name = safeName.lower() + str(i) i += 1 else: name = self.param.name() if (self.paramType == parameters.PARAMETER_TABLE_FIELD or isinstance(self.param, QgsProcessingParameterField)): if self.parentCombo.currentIndex() < 0: QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() datatype = self.datatypeCombo.currentData() default = self.defaultTextBox.text() if not default: default = None self.param = QgsProcessingParameterField(name, description, defaultValue=default, parentLayerParameterName=parent, type=datatype, allowMultiple=self.multipleCheck.isChecked()) elif (self.paramType == parameters.PARAMETER_BAND or isinstance(self.param, QgsProcessingParameterBand)): if self.parentCombo.currentIndex() < 0: QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() self.param = QgsProcessingParameterBand(name, description, None, parent) elif (self.paramType == parameters.PARAMETER_MAP_LAYER or isinstance(self.param, QgsProcessingParameterMapLayer)): self.param = QgsProcessingParameterMapLayer( name, description, types=[self.datatypeCombo.currentData()]) elif (self.paramType == parameters.PARAMETER_RASTER or isinstance(self.param, QgsProcessingParameterRasterLayer)): self.param = QgsProcessingParameterRasterLayer( name, description) elif (self.paramType == parameters.PARAMETER_TABLE or isinstance(self.param, QgsProcessingParameterVectorLayer)): self.param = QgsProcessingParameterVectorLayer( name, description, [self.shapetypeCombo.currentData()]) elif (self.paramType == parameters.PARAMETER_VECTOR or isinstance(self.param, QgsProcessingParameterFeatureSource)): self.param = QgsProcessingParameterFeatureSource( name, description, [self.shapetypeCombo.currentData()]) elif (self.paramType == parameters.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.param = QgsProcessingParameterMultipleLayers( name, description, self.datatypeCombo.currentData()) elif (self.paramType == parameters.PARAMETER_DISTANCE or isinstance(self.param, QgsProcessingParameterDistance)): self.param = QgsProcessingParameterDistance(name, description, self.defaultTextBox.text()) try: vmin = self.minTextBox.text().strip() if not vmin == '': self.param.setMinimum(float(vmin)) vmax = self.maxTextBox.text().strip() if not vmax == '': self.param.setMaximum(float(vmax)) except: QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return if self.parentCombo.currentIndex() < 0: QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() if parent: self.param.setParentParameterName(parent) elif (self.paramType == parameters.PARAMETER_SCALE or isinstance(self.param, QgsProcessingParameterScale)): self.param = QgsProcessingParameterScale(name, description, self.defaultTextBox.text()) elif (self.paramType == parameters.PARAMETER_NUMBER or isinstance(self.param, QgsProcessingParameterNumber)): type = self.type_combo.currentData() self.param = QgsProcessingParameterNumber(name, description, type, self.defaultTextBox.text()) try: vmin = self.minTextBox.text().strip() if not vmin == '': self.param.setMinimum(float(vmin)) vmax = self.maxTextBox.text().strip() if not vmax == '': self.param.setMaximum(float(vmax)) except: QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return elif (self.paramType == parameters.PARAMETER_ENUM or isinstance(self.param, QgsProcessingParameterEnum)): self.param = QgsProcessingParameterEnum(name, description, self.widget.options(), self.widget.allowMultiple(), self.widget.defaultOptions()) elif (self.paramType == parameters.PARAMETER_MATRIX or isinstance(self.param, QgsProcessingParameterMatrix)): self.param = QgsProcessingParameterMatrix(name, description, hasFixedNumberRows=self.widget.fixedRows(), headers=self.widget.headers(), defaultValue=self.widget.value()) # Destination parameter elif (isinstance(self.param, QgsProcessingParameterFeatureSink)): self.param = QgsProcessingParameterFeatureSink( name=name, description=self.param.description(), type=self.param.dataType(), defaultValue=self.defaultWidget.value()) elif (isinstance(self.param, QgsProcessingParameterFileDestination)): self.param = QgsProcessingParameterFileDestination( name=name, description=self.param.description(), fileFilter=self.param.fileFilter(), defaultValue=self.defaultWidget.value()) elif (isinstance(self.param, QgsProcessingParameterFolderDestination)): self.param = QgsProcessingParameterFolderDestination( name=name, description=self.param.description(), defaultValue=self.defaultWidget.value()) elif (isinstance(self.param, QgsProcessingParameterRasterDestination)): self.param = QgsProcessingParameterRasterDestination( name=name, description=self.param.description(), defaultValue=self.defaultWidget.value()) elif (isinstance(self.param, QgsProcessingParameterVectorDestination)): self.param = QgsProcessingParameterVectorDestination( name=name, description=self.param.description(), type=self.param.dataType(), defaultValue=self.defaultWidget.value()) else: if self.paramType: typeId = self.paramType else: typeId = self.param.type() paramTypeDef = QgsApplication.instance().processingRegistry().parameterType(typeId) if not paramTypeDef: msg = self.tr('The parameter `{}` is not registered, are you missing a required plugin?'.format(typeId)) raise UndefinedParameterException(msg) self.param = paramTypeDef.create(name) self.param.setDescription(description) self.param.setMetadata(paramTypeDef.metadata()) if not self.requiredCheck.isChecked(): self.param.setFlags(self.param.flags() | QgsProcessingParameterDefinition.FlagOptional) else: self.param.setFlags(self.param.flags() & ~QgsProcessingParameterDefinition.FlagOptional) if self.advancedCheck.isChecked(): self.param.setFlags(self.param.flags() | QgsProcessingParameterDefinition.FlagAdvanced) else: self.param.setFlags(self.param.flags() & ~QgsProcessingParameterDefinition.FlagAdvanced) settings = QgsSettings() settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) QDialog.accept(self) def reject(self): self.param = None settings = QgsSettings() settings.setValue("/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) QDialog.reject(self)
def restore_default_values_page(self): """Setup UI for default values setting.""" # Clear parameters so it doesn't add parameters when # restore from changes. if self.default_value_parameters: self.default_value_parameters = [] if self.default_value_parameter_containers: self.default_value_parameter_containers = [] for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) default_fields = all_default_fields() for field_group in all_field_groups: settable_fields = [] for field in field_group['fields']: if field not in default_fields: continue else: settable_fields.append(field) default_fields.remove(field) if not settable_fields: continue # Create group box for each field group group_box = QGroupBox(self) group_box.setTitle(field_group['name']) self.container_layout.addWidget(group_box) parameters = [] for settable_field in settable_fields: parameter = self.default_field_to_parameter(settable_field) if parameter: parameters.append(parameter) parameter_container = ParameterContainer( parameters, description_text=field_group['description'], extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) group_box_inner_layout = QVBoxLayout() group_box_inner_layout.addWidget(parameter_container) group_box.setLayout(group_box_inner_layout) # Add to attribute self.default_value_parameter_containers.append(parameter_container) # Only show non-groups default fields if there is one if len(default_fields) > 0: for default_field in default_fields: parameter = self.default_field_to_parameter(default_field) if parameter: self.default_value_parameters.append(parameter) description_text = tr( 'In this options you can change the global default values for ' 'these variables.') parameter_container = ParameterContainer( self.default_value_parameters, description_text=description_text, extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) self.other_group_box = QGroupBox(tr('Non-group fields')) other_group_inner_layout = QVBoxLayout() other_group_inner_layout.addWidget(parameter_container) self.other_group_box.setLayout(other_group_inner_layout) self.container_layout.addWidget(self.other_group_box) # Add to attribute self.default_value_parameter_containers.append(parameter_container)
class ListMultiSelectWidget(QGroupBox): """Widget to show two parallel lists and move elements between the two usage from code: self.myWidget = ListMultiSelectWidget(title='myTitle') self.myLayout.insertWidget(1, self.myWidget) usage from designer: insert a QGroupBox in your UI file optionally give a title to the QGroupBox promote it to ListMultiSelectWidget """ selection_changed = pyqtSignal() def __init__(self, parent=None, title=None): QGroupBox.__init__(self) self.setTitle(title) self.selected_widget = None self.deselected_widget = None self._setupUI() # connect actions self.select_all_btn.clicked.connect(self._select_all) self.deselect_all_btn.clicked.connect(self._deselect_all) self.select_btn.clicked.connect(self._select) self.deselect_btn.clicked.connect(self._deselect) self.deselected_widget.itemDoubleClicked.connect(self._select) self.selected_widget.itemDoubleClicked.connect(self._deselect) def get_selected_items(self): """ :return list with all the selected items text """ return self._get_items(self.selected_widget) def get_deselected_items(self): """ :return list with all the deselected items text """ return self._get_items(self.deselected_widget) def add_selected_items(self, items): """ :param items list of strings to be added in the selected list """ self._add_items(self.selected_widget, items) def add_deselected_items(self, items): """ :param items list of strings to be added in the deselected list """ self._add_items(self.deselected_widget, items) def set_selected_items(self, items): """ :param items list of strings to be set as the selected list """ self._set_items(self.selected_widget, items) def set_deselected_items(self, items): """ :param items list of strings to be set as the deselected list """ self._set_items(self.deselected_widget, items) def clear(self): """ removes all items from selected and deselected """ self.set_selected_items([]) self.set_deselected_items([]) def addItem(self, item): """ This is for Processing :param item: string to be added in the deselected list """ self.add_deselected_items([item]) def addItems(self, items): """ This is for Processing :param items: list of strings to be added in the deselected list """ self.add_deselected_items(items) def _get_items(self, widget): for i in range(widget.count()): yield widget.item(i).text() def _set_items(self, widget, items): widget.clear() self._add_items(widget, items) def _add_items(self, widget, items): widget.addItems(items) def _select_all(self): self.deselected_widget.selectAll() self._do_move(self.deselected_widget, self.selected_widget) def _deselect_all(self): self.selected_widget.selectAll() self._do_move(self.selected_widget, self.deselected_widget) def _select(self): self._do_move(self.deselected_widget, self.selected_widget) def _deselect(self): self._do_move(self.selected_widget, self.deselected_widget) def _do_move(self, fromList, toList): for item in fromList.selectedItems(): prev_from_item = fromList.item(fromList.row(item) - 1) toList.addItem(fromList.takeItem(fromList.row(item))) fromList.scrollToItem(prev_from_item) self.selection_changed.emit() def _setupUI(self): self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # deselected widget self.deselected_widget = QListWidget(self) self._set_list_widget_defaults(self.deselected_widget) deselected_label = QLabel() deselected_label.setText('Deselected') deselected_label.setAlignment(Qt.AlignCenter) deselected_label.setFont(italic_font) deselected_v_layout = QVBoxLayout() deselected_v_layout.addWidget(deselected_label) deselected_v_layout.addWidget(self.deselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText('Selected') selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton('>>') self.deselect_all_btn = SmallQPushButton('<<') self.select_btn = SmallQPushButton('>') self.deselect_btn = SmallQPushButton('<') self.select_btn.setToolTip('Add the selected items') self.deselect_btn.setToolTip('Remove the selected items') self.select_all_btn.setToolTip('Add all') self.deselect_all_btn.setToolTip('Remove all') # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(deselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout) def _set_list_widget_defaults(self, widget): widget.setAlternatingRowColors(True) widget.setSortingEnabled(True) widget.setDragEnabled(True) widget.setDragDropMode(QAbstractItemView.DragDrop) widget.setDragDropOverwriteMode(False) widget.setDefaultDropAction(Qt.MoveAction) widget.setSelectionMode(QAbstractItemView.MultiSelection)
def __init__(self, parent, iface, params, pipe_ft): QDialog.__init__(self, parent) main_lay = QVBoxLayout(self) self.parent = parent self.params = params self.pipe_ft = pipe_ft self.setMinimumWidth(600) self.setMinimumHeight(400) self.setWindowTitle('Pipe section editor') # TODO: softcode self.setWindowModality(QtCore.Qt.ApplicationModal) curr_dir = os.path.dirname(os.path.abspath(__file__)) self.fra_toolbar = QFrame(self) fra_toolbar_lay = QHBoxLayout(self.fra_toolbar) self.btn_zoom = QPushButton('Zoom') self.btn_zoom.clicked.connect(self.btn_zoom_clicked) set_up_button(self.btn_zoom, os.path.join(curr_dir, 'i_zoom.png'), True, 13, 13, 'Zoom') # TODO: softcode self.btn_pan = QPushButton('Pan') self.btn_pan.clicked.connect(self.btn_pan_clicked) set_up_button(self.btn_pan, os.path.join(curr_dir, 'i_pan.png'), True, 15, 15, 'Pan') # TODO: softcode self.btn_home = QPushButton('Full extent') self.btn_home.clicked.connect(self.btn_home_clicked) self.btn_back = QPushButton('Back') self.btn_back.clicked.connect(self.btn_back_clicked) set_up_button(self.btn_back, os.path.join(curr_dir, 'i_back.png'), False, 7, 13, 'Back') # TODO: softcode self.btn_forth = QPushButton('Forth') self.btn_forth.clicked.connect(self.btn_forth_clicked) set_up_button(self.btn_forth, os.path.join(curr_dir, 'i_forth.png'), False, 7, 13, 'Forward') # TODO: softcode self.btn_edit = QPushButton('Edit') self.btn_edit.clicked.connect(self.btn_edit_clicked) fra_toolbar_lay.addWidget(self.btn_zoom) fra_toolbar_lay.addWidget(self.btn_pan) fra_toolbar_lay.addWidget(self.btn_home) fra_toolbar_lay.addWidget(self.btn_back) fra_toolbar_lay.addWidget(self.btn_forth) fra_toolbar_lay.addWidget(self.btn_edit) # Graph canvas self.fra_graph = QFrame(self) self.static_canvas = SectionCanvas(iface, params, self) # Toolbar self.toolbar = NavigationToolbar2QT(self.static_canvas, self) self.toolbar.hide() # OK/Cancel buttons self.fra_buttons = QFrame(self) fra_buttons_lay = QHBoxLayout(self.fra_buttons) self.btn_Cancel = QPushButton('Cancel') self.btn_Ok = QPushButton('OK') fra_buttons_lay.addWidget(self.btn_Ok) fra_buttons_lay.addWidget(self.btn_Cancel) main_lay.addWidget(self.fra_toolbar) main_lay.addWidget(self.static_canvas) main_lay.addWidget(self.fra_buttons) self.setup() self.initialize()
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 switchToCommentTab(self): self.tab.setCurrentIndex(1) self.commentEdit.setFocus() self.commentEdit.selectAll() def setupUi(self): self.checkBoxes = {} self.showAdvanced = False self.wrappers = {} self.valueItems = {} self.dependentItems = {} self.algorithmItem = None self.resize(650, 450) self.mainLayout = QVBoxLayout() self.tab = QTabWidget() self.mainLayout.addWidget(self.tab) 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.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.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.buttonBox.helpRequested.connect(self.openHelp) w = QWidget() w.setLayout(self.verticalLayout2) self.tab.addTab(w, self.tr('Properties')) self.commentLayout = QVBoxLayout() self.commentEdit = QTextEdit() self.commentEdit.setAcceptRichText(False) self.commentLayout.addWidget(self.commentEdit) w2 = QWidget() w2.setLayout(self.commentLayout) self.tab.addTab(w2, self.tr('Comments')) self.mainLayout.addWidget(self.buttonBox) self.setLayout(self.mainLayout) QMetaObject.connectSlotsByName(self) def setComments(self, text): self.commentEdit.setPlainText(text) def comments(self): return self.commentEdit.toPlainText() 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()) output_name = alg.algorithm().outputDefinition(value.outputName()).description() # see if this output has been named by the model designer -- if so, we use that friendly name for name, output in alg.modelOutputs().items(): if output.childOutputName() == value.outputName(): output_name = name break return self.tr("'{0}' from algorithm '{1}'").format(output_name, 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 alg.comment().setDescription(self.comments()) 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 ModelerParametersWidget(QWidget): def __init__(self, alg, model, algName=None, configuration=None, dialog=None, context=None): super().__init__() 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 = context self.dialog = dialog self.widget = ModelerParametersPanelWidget(alg, model, algName, configuration, dialog, context) 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 def algorithm(self): return self._alg def switchToCommentTab(self): self.tab.setCurrentIndex(1) self.commentEdit.setFocus() self.commentEdit.selectAll() def setupUi(self): self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.tab = QTabWidget() self.mainLayout.addWidget(self.tab) self.param_widget = QgsPanelWidgetStack() self.widget.setDockMode(True) self.param_widget.setMainPanel(self.widget) self.tab.addTab(self.param_widget, self.tr('Properties')) self.commentLayout = QVBoxLayout() self.commentEdit = QTextEdit() self.commentEdit.setAcceptRichText(False) self.commentLayout.addWidget(self.commentEdit, 1) hl = QHBoxLayout() hl.setContentsMargins(0, 0, 0, 0) hl.addWidget(QLabel(self.tr('Color'))) self.comment_color_button = QgsColorButton() self.comment_color_button.setAllowOpacity(True) self.comment_color_button.setWindowTitle(self.tr('Comment Color')) self.comment_color_button.setShowNull(True, self.tr('Default')) hl.addWidget(self.comment_color_button) self.commentLayout.addLayout(hl) w2 = QWidget() w2.setLayout(self.commentLayout) self.tab.addTab(w2, self.tr('Comments')) self.setLayout(self.mainLayout) def setComments(self, text): self.commentEdit.setPlainText(text) def comments(self): return self.commentEdit.toPlainText() def setCommentColor(self, color): if color.isValid(): self.comment_color_button.setColor(color) else: self.comment_color_button.setToNull() def commentColor(self): return self.comment_color_button.color( ) if not self.comment_color_button.isNull() else QColor() def getAvailableValuesOfType(self, paramType, outTypes=[], dataTypes=[]): return self.widget.getAvailableValuesOfType(paramType, outTypes, dataTypes) def resolveValueDescription(self, value): return self.widget.resolveValueDescription(value) def setPreviousValues(self): self.widget.setPreviousValues() def createAlgorithm(self): alg = self.widget.createAlgorithm() if alg: alg.comment().setDescription(self.comments()) alg.comment().setColor(self.commentColor()) return alg
class FieldMappingTab(QWidget, object): """Widget class for field mapping.""" def __init__(self, field_group=None, parent=None, iface=None): """Constructor.""" # Init from parent class QWidget.__init__(self, parent) # Attributes self.layer = None self.metadata = {} self.parent = parent self.iface = iface self.field_group = field_group self.setting = QSettings() # TODO(IS): Make dynamic # Main container self.main_layout = QVBoxLayout() # Inner layout self.header_layout = QHBoxLayout() self.content_layout = QHBoxLayout() self.footer_layout = QHBoxLayout() # Header self.header_label = QLabel() self.header_label.setWordWrap(True) # Content self.field_layout = QVBoxLayout() self.parameter_layout = QHBoxLayout() self.field_description = QLabel(tr('List of fields')) self.field_list = QListWidget() self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.field_list.setDragDropMode(QAbstractItemView.DragDrop) self.field_list.setDefaultDropAction(Qt.MoveAction) self.field_list.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) # noinspection PyUnresolvedReferences self.field_list.itemSelectionChanged.connect(self.update_footer) # Footer self.footer_label = QLabel() self.footer_label.setWordWrap(True) # Parameters self.extra_parameters = [(GroupSelectParameter, GroupSelectParameterWidget)] self.parameters = [] self.parameter_container = None # Adding to layout self.header_layout.addWidget(self.header_label) self.field_layout.addWidget(self.field_description) self.field_layout.addWidget(self.field_list) self.field_layout.setSizeConstraint(QLayout.SetMaximumSize) self.content_layout.addLayout(self.field_layout) self.content_layout.addLayout(self.parameter_layout) self.footer_layout.addWidget(self.footer_label) self.main_layout.addLayout(self.header_layout) self.main_layout.addLayout(self.content_layout) self.main_layout.addLayout(self.footer_layout) self.setLayout(self.main_layout) def set_layer(self, layer, keywords=None): """Set layer and update UI accordingly. :param layer: A vector layer that has been already patched with metadata. :type layer: QgsVectorLayer :param keywords: Custom keyword for the layer. :type keywords: dict, None """ self.layer = layer if keywords is not None: self.metadata = keywords else: # Check if it has keywords if not hasattr(layer, 'keywords'): message = 'Layer {layer_name} does not have keywords.'.format( layer_name=layer.name()) raise KeywordNotFoundError(message) self.metadata = layer.keywords self.populate_parameter() def populate_field_list(self, excluded_fields=None): """Helper to add field of the layer to the list. :param excluded_fields: List of field that want to be excluded. :type excluded_fields: list """ # Populate fields list if excluded_fields is None: excluded_fields = [] self.field_list.clear() for field in self.layer.fields(): # Skip if it's excluded if field.name() in excluded_fields: continue # Skip if it's not number (float, int, etc) if field.type() not in qvariant_numbers: continue field_item = QListWidgetItem(self.field_list) field_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) field_item.setData(Qt.UserRole, field.name()) field_item.setText(field.name()) self.field_list.addItem(field_item) def populate_parameter(self): """Helper to setup the parameter widget.""" used_fields = [] self.parameters = [] for field in self.field_group.get('fields', []): selected_option = DO_NOT_REPORT options = OrderedDict([ (DO_NOT_REPORT, { 'label': tr('Do not report'), 'value': None, 'type': STATIC, 'constraint': {} }), ]) # Example: count if field['absolute']: # Used in field options field_label = tr('Count fields') else: # Example: ratio # Used in field options field_label = tr('Ratio fields') global_default_value = get_inasafe_default_value_qsetting( self.setting, GLOBAL, field['key']) options[GLOBAL_DEFAULT] = { 'label': tr('Global default'), 'value': global_default_value, 'type': STATIC, 'constraint': {} } default_custom_value = get_inasafe_default_value_qsetting( self.setting, RECENT, field['key']) custom_value = self.metadata.get('inasafe_default_values', {}).get( field['key'], default_custom_value) if field['key'] in self.metadata.get('inasafe_default_values', {}): if custom_value == global_default_value: selected_option = GLOBAL_DEFAULT else: selected_option = CUSTOM_VALUE min_value = field['default_value'].get('min_value', 0) max_value = field['default_value'].get('max_value', 100) default_step = (max_value - min_value) / 100.0 step = field['default_value'].get('increment', default_step) options[CUSTOM_VALUE] = { 'label': tr('Custom'), 'value': custom_value, 'type': SINGLE_DYNAMIC, 'constraint': { 'min': min_value, 'max': max_value, 'step': step } } custom_fields = self.metadata.get('inasafe_fields', {}).get(field['key'], []) if field['key'] in self.metadata.get('inasafe_fields', {}): selected_option = FIELDS if isinstance(custom_fields, str): custom_fields = [custom_fields] options[FIELDS] = { 'label': field_label, 'value': custom_fields, 'type': MULTIPLE_DYNAMIC, 'constraint': {} } used_fields.extend(custom_fields) parameter = GroupSelectParameter() parameter.guid = field['key'] parameter.name = field['name'] parameter.options = options parameter.selected = selected_option parameter.help_text = field['help_text'] parameter.description = field['description'] self.parameters.append(parameter) self.parameter_container = ParameterContainer( parameters=self.parameters, extra_parameters=self.extra_parameters, vertical=False) self.parameter_container.setup_ui() constraints = self.field_group.get('constraints', {}) for key, value in list(constraints.items()): self.parameter_container.add_validator( validators[key], kwargs=value['kwargs'], validation_message=value['message']) self.parameter_layout.addWidget(self.parameter_container) default_ratio_help_text = tr( 'By default, InaSAFE will calculate the default ratio ' 'however users have the option to include this in the ' 'analysis report. If you do not want to see the default ' 'results in the report choose "do not report".') # Set move or copy if self.field_group.get('exclusive', False): # If exclusive, do not add used field. self.populate_field_list(excluded_fields=used_fields) # Use move action since it's exclusive self.field_list.setDefaultDropAction(Qt.MoveAction) # Just make sure that the signal is disconnected try: # noinspection PyUnresolvedReferences self.field_list.itemChanged.disconnect(self.drop_remove) except TypeError: pass # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can only map one field to one concept.') else: # If not exclusive, add all field. self.populate_field_list() # Use copy action since it's not exclusive self.field_list.setDefaultDropAction(Qt.CopyAction) # noinspection PyUnresolvedReferences self.field_list.itemChanged.connect( partial(self.drop_remove, field_list=self.field_list)) self.connect_drop_remove_parameter() # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can map one field to more than one concepts.') self.header_label.setText(header_text) def get_parameter_value(self): """Get parameter of the tab. :returns: Dictionary of parameters by type in this format: {'fields': {}, 'values': {}}. :rtype: dict """ parameters = self.parameter_container.get_parameters(True) field_parameters = {} value_parameters = {} for parameter in parameters: if parameter.selected_option_type() in [SINGLE_DYNAMIC, STATIC]: value_parameters[parameter.guid] = parameter.value elif parameter.selected_option_type() == MULTIPLE_DYNAMIC: field_parameters[parameter.guid] = parameter.value return {'fields': field_parameters, 'values': value_parameters} def update_footer(self): """Update footer when the field list change.""" field_item = self.field_list.currentItem() if not field_item: self.footer_label.setText('') return field_name = field_item.data(Qt.UserRole) field = self.layer.fields().field(field_name) index = self.layer.fields().lookupField(field_name) unique_values = list(self.layer.uniqueValues(index)) pretty_unique_values = ', '.join([str(v) for v in unique_values[:10]]) footer_text = tr('Field type: {0}\n').format(field.typeName()) footer_text += tr('Unique values: {0}').format(pretty_unique_values) self.footer_label.setText(footer_text) def connect_drop_remove_parameter(self): parameter_widgets = self.parameter_container.get_parameter_widgets() for parameter_widget in parameter_widgets: field_list = parameter_widget.widget().list_widget field_list.itemChanged.connect( partial(self.drop_remove, field_list=field_list)) @staticmethod def drop_remove(*args, **kwargs): """Action when we need to remove dropped item. :param *args: Position arguments. :type *args: list :param kwargs: Keywords arguments. :type kwargs: dict """ dropped_item = args[0] field_list = kwargs['field_list'] num_duplicate = 0 for i in range(field_list.count()): if dropped_item.text() == field_list.item(i).text(): num_duplicate += 1 if num_duplicate > 1: # Notes(IS): For some reason, removeItemWidget is not working. field_list.takeItem(field_list.row(dropped_item))
def createWidgets(self): vlayout = QVBoxLayout() vlayout.addWidget(QLabel("The analysis was performed successfully.")) vlayout.addStretch(1) vlayout.addWidget(QLabel("Click 'Restart' to do new analysis or 'Close' to close.")) self.setLayout(vlayout)
class ModelerParametersPanelWidget(QgsPanelWidget): def __init__(self, alg, model, algName=None, configuration=None, dialog=None, context=None): super().__init__() 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 = context self.dialog = dialog 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 def algorithm(self): return self._alg def setupUi(self): self.showAdvanced = False self.wrappers = {} self.algorithmItem = None self.mainLayout = QVBoxLayout() self.mainLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout = QVBoxLayout() self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.verticalLayout.addWidget(self.bar) hLayout = QHBoxLayout() hLayout.setContentsMargins(0, 0, 0, 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.setActiveLayer(iface.activeLayer()) 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.dialog) 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: 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 output in self._alg.destinationParameterDefinitions(): if output.flags() & QgsProcessingParameterDefinition.FlagHidden: continue widget = QgsGui.processingGuiRegistry( ).createModelerParameterWidget(self.model, self.childId, output, self.context) widget.setDialog(self.dialog) widget.setWidgetContext(widget_context) widget.registerProcessingContextGenerator(self.context_generator) self.wrappers[output.name()] = widget item = QgsFilterLineEdit() if hasattr(item, 'setPlaceholderText'): item.setPlaceholderText( self.tr('[Enter name if this is a final result]')) label = widget.createLabel() if label is not None: self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(widget) label = QLabel(' ') self.verticalLayout.addWidget(label) label = QLabel(self.tr('Dependencies')) self.dependencies_panel = QgsModelChildDependenciesWidget( self, self.model, self.childId) self.verticalLayout.addWidget(label) self.verticalLayout.addWidget(self.dependencies_panel) self.verticalLayout.addStretch(1000) self.setPreviousValues() 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.scrollArea.setFrameStyle(QFrame.NoFrame) self.verticalLayout2.addWidget(self.scrollArea) w = QWidget() w.setLayout(self.verticalLayout2) self.mainLayout.addWidget(w) self.setLayout(self.mainLayout) 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()) output_name = alg.algorithm().outputDefinition( value.outputName()).description() # see if this output has been named by the model designer -- if so, we use that friendly name for name, output in alg.modelOutputs().items(): if output.childOutputName() == value.outputName(): output_name = name break return self.tr("'{0}' from algorithm '{1}'").format( output_name, 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 output in self.algorithm().destinationParameterDefinitions(): if output.flags( ) & QgsProcessingParameterDefinition.FlagHidden: continue model_output_name = None for name, out in alg.modelOutputs().items(): if out.childId() == self.childId and out.childOutputName( ) == output.name(): # this destination parameter is linked to a model output model_output_name = out.name() break value = None if model_output_name is None and output.name( ) in alg.parameterSources(): value = alg.parameterSources()[output.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[output.name()] if model_output_name is not None: wrapper.setToModelOutput(model_output_name) elif value is not None or output.defaultValue() is not None: if value is None: value = QgsProcessingModelChildParameterSource.fromStaticValue( output.defaultValue()) wrapper.setWidgetValue(value) self.dependencies_panel.setValue(alg.dependencies()) 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: val = 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) ] valid = True 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): valid = False break if valid: alg.addParameterSources(param.name(), val) outputs = {} for output in self._alg.destinationParameterDefinitions(): if not output.flags( ) & QgsProcessingParameterDefinition.FlagHidden: wrapper = self.wrappers[output.name()] if wrapper.isModelOutput(): name = wrapper.modelOutputName() if name: model_output = QgsProcessingModelOutput(name, name) model_output.setChildId(alg.childId()) model_output.setChildOutputName(output.name()) outputs[name] = model_output else: val = wrapper.value() if isinstance(val, QgsProcessingModelChildParameterSource): val = [val] alg.addParameterSources(output.name(), val) if output.flags( ) & QgsProcessingParameterDefinition.FlagIsModelOutput: if output.name() not in outputs: model_output = QgsProcessingModelOutput( output.name(), output.name()) model_output.setChildId(alg.childId()) model_output.setChildOutputName(output.name()) outputs[output.name()] = model_output alg.setModelOutputs(outputs) alg.setDependencies(self.dependencies_panel.value()) return alg
class LoadOutputAsLayerDialog(QDialog, FORM_CLASS): """ Dialog to load an oq-engine output as layer """ init_done = pyqtSignal(QDialog) loading_completed = pyqtSignal(QDialog) loading_exception = pyqtSignal(QDialog, Exception) def __init__(self, drive_engine_dlg, iface, viewer_dock, session, hostname, calc_id, output_type=None, path=None, mode=None, zonal_layer_path=None, engine_version=None, calculation_mode=None): # sanity check if output_type not in OQ_TO_LAYER_TYPES: raise NotImplementedError(output_type) self.drive_engine_dlg = drive_engine_dlg self.iface = iface self.viewer_dock = viewer_dock self.path = path self.session = session self.hostname = hostname self.calc_id = calc_id self.output_type = output_type self.mode = mode # if 'testing' it will avoid some user interaction self.zonal_layer_path = zonal_layer_path self.engine_version = engine_version self.calculation_mode = calculation_mode QDialog.__init__(self) # Set up the user interface from Designer. self.setupUi(self) # Disable ok_button until all user options are set self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok) self.ok_button.setDisabled(True) self.oqparam = self.drive_engine_dlg.get_oqparam() def on_extract_error(self, exception): if isinstance(exception, TaskCanceled): msg = 'Data extraction canceled' log_msg(msg, level='W', message_bar=self.iface.messageBar()) else: log_msg('Unable to complete data extraction', level='C', message_bar=self.iface.messageBar(), exception=exception) self.reject() def finalize_init(self, extracted_npz): self.npz_file = extracted_npz self.populate_out_dep_widgets() self.adjustSize() self.set_ok_button() self.show() self.init_done.emit(self) def create_num_sites_indicator(self): self.num_sites_msg = 'Number of sites: %s' self.num_sites_lbl = QLabel(self.num_sites_msg % '') self.vlayout.addWidget(self.num_sites_lbl) def create_file_size_indicator(self): self.file_size_msg = 'File size: %s' self.file_size_lbl = QLabel(self.file_size_msg % '') self.vlayout.addWidget(self.file_size_lbl) def create_single_layer_ckb(self): self.load_single_layer_ckb = QCheckBox( 'Load one layer containing all hazard maps') self.vlayout.addWidget(self.load_single_layer_ckb) def create_load_one_layer_per_stat_ckb(self): self.load_one_layer_per_stat_ckb = QCheckBox( 'Load one layer per realization or statistic') self.vlayout.addWidget(self.load_one_layer_per_stat_ckb) def create_min_mag_dsb(self, min_mag=4.0): self.min_mag_lbl = QLabel() self.min_mag_dsb = QDoubleSpinBox(self) self.min_mag_dsb.setRange(0, 10) self.min_mag_dsb.setDecimals(1) self.min_mag_dsb.setSingleStep(0.1) self.min_mag_dsb.setValue(min_mag) self.vlayout.addWidget(self.min_mag_lbl) self.vlayout.addWidget(self.min_mag_dsb) # NOTE: if we don't modify the text of the label after adding the # widget to the layout, the adjustSize does not work properly, for some # unknown reason self.min_mag_lbl.setText('Minimum magnitude') def create_rlz_or_stat_selector(self, all_ckb=False, label='Realization'): self.rlz_or_stat_lbl = QLabel(label) self.rlz_or_stat_cbx = QComboBox() self.rlz_or_stat_cbx.setEnabled(False) self.rlz_or_stat_cbx.currentIndexChanged['QString'].connect( self.on_rlz_or_stat_changed) if all_ckb: self.load_all_rlzs_or_stats_chk = QCheckBox( 'Load all realizations') self.load_all_rlzs_or_stats_chk.stateChanged[int].connect( self.on_load_all_rlzs_or_stats_chk_stateChanged) self.vlayout.addWidget(self.load_all_rlzs_or_stats_chk) self.vlayout.addWidget(self.rlz_or_stat_lbl) self.vlayout.addWidget(self.rlz_or_stat_cbx) def on_load_all_rlzs_or_stats_chk_stateChanged(self, state): self.rlz_or_stat_cbx.setEnabled(state == Qt.Unchecked) def create_selector(self, name, label_text, filter_ckb=False, add_to_layout=None, on_text_changed=None): if add_to_layout is not None: layout = add_to_layout else: layout = self.vlayout setattr(self, "%s_lbl" % name, QLabel(label_text)) setattr(self, "%s_cbx" % name, QComboBox()) lbl = getattr(self, "%s_lbl" % name) cbx = getattr(self, "%s_cbx" % name) cbx.setDisabled(filter_ckb) if on_text_changed is not None: cbx.currentTextChanged['QString'].connect(on_text_changed) if filter_ckb: setattr(self, "filter_by_%s_ckb" % name, QCheckBox('Filter by %s' % name)) filter_ckb = getattr(self, "filter_by_%s_ckb" % name) def on_load_all_ckb_changed(): cbx.setEnabled(filter_ckb.isChecked()) filter_ckb.stateChanged[int].connect(on_load_all_ckb_changed) filter_ckb.setChecked(False) layout.addWidget(filter_ckb) layout.addWidget(lbl) layout.addWidget(cbx) def create_imt_selector(self, all_ckb=False): self.imt_lbl = QLabel('Intensity Measure Type') self.imt_cbx = QComboBox() self.imt_cbx.setEnabled(False) self.imt_cbx.currentIndexChanged['QString'].connect( self.on_imt_changed) if all_ckb: self.load_all_imts_chk = QCheckBox('Load all IMTs') self.load_all_imts_chk.stateChanged[int].connect( self.on_load_all_imts_chk_stateChanged) self.vlayout.addWidget(self.load_all_imts_chk) self.vlayout.addWidget(self.imt_lbl) self.vlayout.addWidget(self.imt_cbx) def on_load_all_imts_chk_stateChanged(self, state): self.imt_cbx.setEnabled(state == Qt.Unchecked) def create_poe_selector(self, all_ckb=False): self.poe_lbl = QLabel('Probability of Exceedance') self.poe_cbx = QComboBox() self.poe_cbx.setEnabled(False) self.poe_cbx.currentIndexChanged['QString'].connect( self.on_poe_changed) if all_ckb: self.load_all_poes_chk = QCheckBox('Load all PoEs') self.load_all_poes_chk.stateChanged[int].connect( self.on_load_all_poes_chk_stateChanged) self.vlayout.addWidget(self.load_all_poes_chk) self.vlayout.addWidget(self.poe_lbl) self.vlayout.addWidget(self.poe_cbx) def on_load_all_poes_chk_stateChanged(self, state): self.poe_cbx.setEnabled(state == Qt.Unchecked) def create_loss_type_selector(self): self.loss_type_lbl = QLabel('Loss Type') self.loss_type_cbx = QComboBox() self.loss_type_cbx.setEnabled(False) self.loss_type_cbx.currentIndexChanged['QString'].connect( self.on_loss_type_changed) self.vlayout.addWidget(self.loss_type_lbl) self.vlayout.addWidget(self.loss_type_cbx) def create_eid_selector(self): self.eid_lbl = QLabel('Event ID') self.eid_sbx = QSpinBox() self.eid_sbx.setEnabled(False) self.vlayout.addWidget(self.eid_lbl) self.vlayout.addWidget(self.eid_sbx) def create_dmg_state_selector(self): self.dmg_state_lbl = QLabel('Damage state') self.dmg_state_cbx = QComboBox() self.dmg_state_cbx.setEnabled(False) self.dmg_state_cbx.currentIndexChanged['QString'].connect( self.on_dmg_state_changed) self.vlayout.addWidget(self.dmg_state_lbl) self.vlayout.addWidget(self.dmg_state_cbx) def create_taxonomy_selector(self): self.taxonomy_lbl = QLabel('Taxonomy') self.taxonomy_cbx = QComboBox() self.taxonomy_cbx.setEnabled(False) self.vlayout.addWidget(self.taxonomy_lbl) self.vlayout.addWidget(self.taxonomy_cbx) def create_style_by_selector(self): self.style_by_lbl = QLabel('Style by') self.style_by_cbx = QComboBox() self.vlayout.addWidget(self.style_by_lbl) self.vlayout.addWidget(self.style_by_cbx) def create_load_selected_only_ckb(self): self.load_selected_only_ckb = QCheckBox("Load only the selected items") self.load_selected_only_ckb.setChecked(True) self.vlayout.addWidget(self.load_selected_only_ckb) def create_show_return_period_ckb(self): self.show_return_period_chk = QCheckBox( "Show the return period in layer names") self.show_return_period_chk.setChecked(False) self.vlayout.addWidget(self.show_return_period_chk) def create_aggregate_by_site_ckb(self): self.aggregate_by_site_ckb = QCheckBox("Aggregate by site") self.aggregate_by_site_ckb.setChecked(True) self.vlayout.addWidget(self.aggregate_by_site_ckb) def create_zonal_layer_selector(self, discard_nonmatching=True): self.added_zonal_layer = None self.zonal_layer_gbx = QGroupBox() self.zonal_layer_gbx.setTitle('Aggregate by zone') self.zonal_layer_gbx.setCheckable(True) self.zonal_layer_gbx.setChecked(False) self.zonal_layer_gbx_v_layout = QVBoxLayout() self.zonal_layer_gbx.setLayout(self.zonal_layer_gbx_v_layout) self.zonal_layer_cbx = QComboBox() self.zonal_layer_cbx.addItem('') self.zonal_layer_lbl = QLabel('Zonal layer') self.zonal_layer_tbn = QToolButton() self.zonal_layer_tbn.setText('...') self.discard_nonmatching_chk = QCheckBox( 'Discard zones with no points') self.discard_nonmatching_chk.setChecked(discard_nonmatching) self.zonal_layer_h_layout = QHBoxLayout() self.zonal_layer_h_layout.addWidget(self.zonal_layer_cbx) self.zonal_layer_h_layout.addWidget(self.zonal_layer_tbn) self.zonal_layer_gbx_v_layout.addWidget(self.zonal_layer_lbl) self.zonal_layer_gbx_v_layout.addLayout(self.zonal_layer_h_layout) self.zonal_layer_gbx_v_layout.addWidget(self.discard_nonmatching_chk) self.vlayout.addWidget(self.zonal_layer_gbx) self.zonal_layer_tbn.clicked.connect(self.open_load_zonal_layer_dialog) self.zonal_layer_cbx.currentIndexChanged[int].connect( self.on_zonal_layer_cbx_currentIndexChanged) self.zonal_layer_gbx.toggled[bool].connect( self.on_zonal_layer_gbx_toggled) self.iface.layerTreeView().currentLayerChanged.connect( self.on_currentLayerChanged) def on_currentLayerChanged(self): self.pre_populate_zonal_layer_cbx() def pre_populate_zonal_layer_cbx(self): # populate cbx only with vector layers containing polygons self.zonal_layer_cbx.clear() for key, layer in QgsProject.instance().mapLayers().items(): if layer.type() != QgsMapLayer.VectorLayer: continue if layer.geometryType() == QgsWkbTypes.PolygonGeometry: self.zonal_layer_cbx.addItem(layer.name()) self.zonal_layer_cbx.setItemData( self.zonal_layer_cbx.count() - 1, layer.id()) if self.added_zonal_layer is not None: self.zonal_layer_cbx.setCurrentIndex( self.zonal_layer_cbx.findData(self.added_zonal_layer.id())) self.zonal_layer_gbx.setChecked( self.zonal_layer_cbx.currentIndex() != -1) def on_zonal_layer_cbx_currentIndexChanged(self, new_index): self.zonal_layer = None if not self.zonal_layer_cbx.currentText(): if self.zonal_layer_gbx.isChecked(): self.ok_button.setEnabled(False) return zonal_layer_id = self.zonal_layer_cbx.itemData(new_index) self.zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id) self.set_ok_button() def on_zonal_layer_gbx_toggled(self, is_checked): if is_checked and not self.zonal_layer_cbx.currentText(): self.ok_button.setEnabled(False) else: self.set_ok_button() def on_output_type_changed(self): if self.output_type in OQ_TO_LAYER_TYPES: self.create_load_selected_only_ckb() self.set_ok_button() def on_rlz_or_stat_changed(self): self.dataset = self.npz_file[self.rlz_or_stat_cbx.currentText()] self.set_ok_button() def on_loss_type_changed(self): self.set_ok_button() def on_imt_changed(self): self.set_ok_button() def on_poe_changed(self): self.set_ok_button() def on_eid_changed(self): self.set_ok_button() def on_dmg_state_changed(self): self.set_ok_button() def populate_out_dep_widgets(self): self.populate_rlz_or_stat_cbx() self.show_num_sites() def get_taxonomies(self): raise NotImplementedError() def populate_rlz_or_stat_cbx(self): self.rlzs_or_stats = [ key for key in sorted(self.npz_file) if key not in ('imtls', 'array') ] self.rlz_or_stat_cbx.clear() self.rlz_or_stat_cbx.setEnabled(True) self.rlz_or_stat_cbx.addItems(self.rlzs_or_stats) def populate_loss_type_cbx(self, loss_types): self.loss_type_cbx.clear() self.loss_type_cbx.setEnabled(True) self.loss_type_cbx.addItems(loss_types) def show_num_sites(self): # NOTE: we are assuming all realizations have the same number of sites, # which currently is always true. # If different realizations have a different number of sites, we # need to move this block of code inside on_rlz_or_stat_changed() rlz_or_stat_data = self.npz_file[self.rlz_or_stat_cbx.currentText()] self.num_sites_lbl.setText(self.num_sites_msg % rlz_or_stat_data.shape) def set_ok_button(self): raise NotImplementedError() def build_layer_name(self, *args, **kwargs): raise NotImplementedError() def get_field_types(self, **kwargs): raise NotImplementedError() def read_npz_into_layer(self, field_types, **kwargs): raise NotImplementedError() def load_from_npz(self): raise NotImplementedError() def add_field_to_layer(self, field_name, field_type): # NOTE: add_attribute use the native qgis editing manager added_field_name = add_attribute(field_name, field_type, self.layer) return added_field_name def get_investigation_time(self): if self.output_type in ('hcurves', 'uhs', 'hmaps', 'ruptures'): try: investigation_time = self.npz_file['investigation_time'] except KeyError as exc: msg = ('investigation_time not found. It is mandatory for %s.' ' Please check if the ouptut was produced by an' ' obsolete version of the OpenQuake Engine' ' Server.') % self.output_type log_msg(msg, level='C', message_bar=self.iface.messageBar(), exception=exc) else: # We must cast to 'str' to keep numerical padding # after saving the project return str(investigation_time) else: # some outputs do not need the investigation time return None def build_layer(self, rlz_or_stat=None, taxonomy=None, poe=None, loss_type=None, dmg_state=None, gsim=None, imt=None, boundaries=None, geometry_type='point', wkt_geom_type=None, row_wkt_geom_types=None, add_to_group=None, add_to_map=True): layer_name = self.build_layer_name(rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, gsim=gsim, imt=imt, geometry_type=geometry_type) field_types = self.get_field_types(rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, imt=imt) # create layer self.layer = QgsVectorLayer("%s?crs=epsg:4326" % geometry_type, layer_name, "memory") modified_field_types = copy.copy(field_types) for field_name, field_type in field_types.items(): if field_name in ['lon', 'lat', 'boundary']: continue added_field_name = self.add_field_to_layer(field_name, field_type) if field_name != added_field_name: if field_name == self.default_field_name: self.default_field_name = added_field_name # replace field_name with the actual added_field_name del modified_field_types[field_name] modified_field_types[added_field_name] = field_type field_types = copy.copy(modified_field_types) self.layer = self.read_npz_into_layer( field_types, rlz_or_stat=rlz_or_stat, taxonomy=taxonomy, poe=poe, loss_type=loss_type, dmg_state=dmg_state, imt=imt, boundaries=boundaries, geometry_type=geometry_type, wkt_geom_type=wkt_geom_type, row_wkt_geom_types=row_wkt_geom_types) if (self.output_type == 'damages-rlzs' and not self.aggregate_by_site_ckb.isChecked()): self.layer.setCustomProperty('output_type', 'recovery_curves') else: self.layer.setCustomProperty('output_type', self.output_type) investigation_time = self.get_investigation_time() if investigation_time is not None: self.layer.setCustomProperty('investigation_time', investigation_time) if self.engine_version is not None: self.layer.setCustomProperty('engine_version', self.engine_version) irmt_version = get_irmt_version() self.layer.setCustomProperty('irmt_version', irmt_version) self.layer.setCustomProperty('calc_id', self.calc_id) if poe is not None: self.layer.setCustomProperty('poe', poe) user_params = { 'rlz_or_stat': rlz_or_stat, 'taxonomy': taxonomy, 'poe': poe, 'loss_type': loss_type, 'dmg_state': dmg_state, 'gsim': gsim, 'imt': imt } write_metadata_to_layer(self.drive_engine_dlg, self.output_type, self.layer, user_params) try: if (self.zonal_layer_cbx.currentText() and self.zonal_layer_gbx.isChecked()): return except AttributeError: # the aggregation stuff might not exist for some loaders pass if add_to_map: if add_to_group: tree_node = add_to_group else: tree_node = QgsProject.instance().layerTreeRoot() if self.mode != 'testing': # NOTE: the following commented line would cause (unexpectedly) # "QGIS died on signal 11" and double creation of some # layers during integration tests QgsProject.instance().addMapLayer(self.layer, False) tree_node.insertLayer(0, self.layer) self.iface.setActiveLayer(self.layer) if add_to_group: # NOTE: zooming to group from caller function, to avoid # repeating it once per layer pass else: self.iface.zoomToActiveLayer() log_msg('Layer %s was created successfully' % layer_name, level='S', message_bar=self.iface.messageBar()) return self.layer @staticmethod def style_maps(layer, style_by, iface, output_type='damages-rlzs', perils=None, add_null_class=False, render_higher_on_top=False, repaint=True, use_sgc_style=False): symbol = QgsSymbol.defaultSymbol(layer.geometryType()) # see properties at: # https://qgis.org/api/qgsmarkersymbollayerv2_8cpp_source.html#l01073 symbol.setOpacity(1) if isinstance(symbol, QgsMarkerSymbol): # do it only for the layer with points symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen)) style = get_style(layer, iface.messageBar()) # this is the default, as specified in the user settings ramp = QgsGradientColorRamp(style['color_from'], style['color_to']) style_mode = style['style_mode'] # in most cases, we override the user-specified setting, and use # instead a setting that was required by scientists if output_type in OQ_TO_LAYER_TYPES: default_qgs_style = QgsStyle().defaultStyle() default_color_ramp_names = default_qgs_style.colorRampNames() if output_type in ( 'damages-rlzs', 'avg_losses-rlzs', 'avg_losses-stats', ): # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Jenks else: style_mode = 'Jenks' ramp_type_idx = default_color_ramp_names.index('Reds') symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top'])) inverted = False elif (output_type in ('gmf_data', 'ruptures') or (output_type == 'hmaps' and not use_sgc_style)): # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if output_type == 'ruptures': if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Pretty else: style_mode = 'PrettyBreaks' else: if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.EqualInterval else: style_mode = 'EqualInterval' ramp_type_idx = default_color_ramp_names.index('Spectral') inverted = True symbol.setColor(QColor(RAMP_EXTREME_COLORS['Reds']['top'])) elif output_type == 'hmaps' and use_sgc_style: # FIXME: for SGC they were using size 10000 map units # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.Pretty else: style_mode = 'PrettyBreaks' try: ramp_type_idx = default_color_ramp_names.index( 'SGC_Green2Red_Hmap_Color_Ramp') except ValueError: raise ValueError( 'Color ramp SGC_Green2Red_Hmap_Color_Ramp was ' 'not found. Please import it from ' 'Settings -> Style Manager, loading ' 'svir/resources/sgc_green2red_hmap_color_ramp.xml') inverted = False registry = QgsApplication.symbolLayerRegistry() symbol_props = { 'name': 'square', 'color': '0,0,0', 'color_border': '0,0,0', 'offset': '0,0', 'size': '1.5', # FIXME 'angle': '0', } square = registry.symbolLayerMetadata( "SimpleMarker").createSymbolLayer(symbol_props) symbol = QgsSymbol.defaultSymbol(layer.geometryType()).clone() symbol.deleteSymbolLayer(0) symbol.appendSymbolLayer(square) symbol.symbolLayer(0).setStrokeStyle(Qt.PenStyle(Qt.NoPen)) elif output_type in ['asset_risk', 'input']: # options are EqualInterval, Quantile, Jenks, StdDev, Pretty # jenks = natural breaks if Qgis.QGIS_VERSION_INT < 31000: style_mode = QgsGraduatedSymbolRenderer.EqualInterval else: style_mode = 'EqualInterval' # exposure_strings = ['number', 'occupants', 'value'] # setting exposure colors by default colors = { 'single': RAMP_EXTREME_COLORS['Blues']['top'], 'ramp_name': 'Blues' } inverted = False if output_type == 'asset_risk': damage_strings = perils for damage_string in damage_strings: if damage_string in style_by: colors = { 'single': RAMP_EXTREME_COLORS['Spectral']['top'], 'ramp_name': 'Spectral' } inverted = True break else: # 'input' colors = { 'single': RAMP_EXTREME_COLORS['Greens']['top'], 'ramp_name': 'Greens' } symbol.symbolLayer(0).setShape( QgsSimpleMarkerSymbolLayerBase.Square) single_color = colors['single'] ramp_name = colors['ramp_name'] ramp_type_idx = default_color_ramp_names.index(ramp_name) symbol.setColor(QColor(single_color)) else: raise NotImplementedError( 'Undefined color ramp for output type %s' % output_type) ramp = default_qgs_style.colorRamp( default_color_ramp_names[ramp_type_idx]) if inverted: ramp.invert() # get unique values fni = layer.fields().indexOf(style_by) unique_values = layer.dataProvider().uniqueValues(fni) num_unique_values = len(unique_values - {NULL}) if num_unique_values > 2: if Qgis.QGIS_VERSION_INT < 31000: renderer = QgsGraduatedSymbolRenderer.createRenderer( layer, style_by, min(num_unique_values, style['classes']), style_mode, symbol.clone(), ramp) else: renderer = QgsGraduatedSymbolRenderer(style_by, []) # NOTE: the following returns an instance of one of the # subclasses of QgsClassificationMethod classification_method = \ QgsApplication.classificationMethodRegistry().method( style_mode) renderer.setClassificationMethod(classification_method) renderer.updateColorRamp(ramp) renderer.updateSymbols(symbol.clone()) renderer.updateClasses( layer, min(num_unique_values, style['classes'])) if not use_sgc_style: if Qgis.QGIS_VERSION_INT < 31000: label_format = renderer.labelFormat() # NOTE: the following line might be useful # label_format.setTrimTrailingZeroes(True) label_format.setPrecision(2) renderer.setLabelFormat(label_format, updateRanges=True) else: renderer.classificationMethod().setLabelPrecision(2) renderer.calculateLabelPrecision() elif num_unique_values == 2: categories = [] for unique_value in unique_values: symbol = symbol.clone() try: symbol.setColor( QColor(RAMP_EXTREME_COLORS[ramp_name] ['bottom' if unique_value == min(unique_values) else 'top'])) except Exception: symbol.setColor( QColor(style['color_from'] if unique_value == min(unique_values) else style['color_to'])) category = QgsRendererCategory(unique_value, symbol, str(unique_value)) # entry for the list of category items categories.append(category) renderer = QgsCategorizedSymbolRenderer(style_by, categories) else: renderer = QgsSingleSymbolRenderer(symbol.clone()) if add_null_class and NULL in unique_values: # add a class for NULL values rule_renderer = QgsRuleBasedRenderer(symbol.clone()) root_rule = rule_renderer.rootRule() not_null_rule = root_rule.children()[0].clone() # strip parentheses from stringified color HSL not_null_rule.setFilterExpression( '%s IS NOT NULL' % QgsExpression.quotedColumnRef(style_by)) not_null_rule.setLabel('%s:' % style_by) root_rule.appendChild(not_null_rule) null_rule = root_rule.children()[0].clone() null_rule.setSymbol( QgsFillSymbol.createSimple({ 'color': '160,160,160', 'style': 'diagonal_x' })) null_rule.setFilterExpression( '%s IS NULL' % QgsExpression.quotedColumnRef(style_by)) null_rule.setLabel(tr('No points')) root_rule.appendChild(null_rule) if isinstance(renderer, QgsGraduatedSymbolRenderer): # create value ranges rule_renderer.refineRuleRanges(not_null_rule, renderer) # remove default rule elif isinstance(renderer, QgsCategorizedSymbolRenderer): rule_renderer.refineRuleCategoris(not_null_rule, renderer) for rule in rule_renderer.rootRule().children()[1].children(): label = rule.label() # by default, labels are like: # ('"collapse-structural-ASH_DRY_sum" >= 0.0000 AND # "collapse-structural-ASH_DRY_sum" <= 2.3949') first, second = label.split(" AND ") bottom = first.rsplit(" ", 1)[1] top = second.rsplit(" ", 1)[1] simplified = "%s - %s" % (bottom, top) rule.setLabel(simplified) root_rule.removeChildAt(0) renderer = rule_renderer if render_higher_on_top: renderer.setUsingSymbolLevels(True) symbol_items = [item for item in renderer.legendSymbolItems()] for i in range(len(symbol_items)): sym = symbol_items[i].symbol().clone() key = symbol_items[i].ruleKey() for lay in range(sym.symbolLayerCount()): sym.symbolLayer(lay).setRenderingPass(i) renderer.setLegendSymbolItem(key, sym) layer.setRenderer(renderer) if not use_sgc_style: layer.setOpacity(0.7) if repaint: layer.triggerRepaint() iface.setActiveLayer(layer) iface.zoomToActiveLayer() # NOTE QGIS3: probably not needed # iface.layerTreeView().refreshLayerSymbology(layer.id()) iface.mapCanvas().refresh() def style_categorized(self, layer=None, style_by=None): if layer is None: layer = self.layer if style_by is None: style_by = self.default_field_name # get unique values fni = layer.fields().indexOf(style_by) unique_values = layer.dataProvider().uniqueValues(fni) # define categories categories = [] for unique_value in unique_values: # initialize the default symbol for this geometry type symbol = QgsSymbol.defaultSymbol(layer.geometryType()) # configure a symbol layer layer_style = {} layer_style['color'] = '%d, %d, %d' % (randrange( 0, 256), randrange(0, 256), randrange(0, 256)) layer_style['outline'] = '#000000' symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style) # replace default symbol layer with the configured one if symbol_layer is not None: symbol.changeSymbolLayer(0, symbol_layer) # create renderer object category = QgsRendererCategory(unique_value, symbol, str(unique_value)) # entry for the list of category items categories.append(category) # create renderer object renderer = QgsCategorizedSymbolRenderer(style_by, categories) # assign the created renderer to the layer if renderer is not None: layer.setRenderer(renderer) layer.triggerRepaint() # NOTE QGIS3: probably not needed # self.iface.layerTreeView().refreshLayerSymbology(layer.id()) self.iface.mapCanvas().refresh() def style_curves(self): registry = QgsApplication.symbolLayerRegistry() symbol_props = { 'name': 'cross2', 'color': '0,0,0', 'color_border': '0,0,0', 'offset': '0,0', 'size': '1.5', 'angle': '0', } opacity = 0.7 cross = registry.symbolLayerMetadata("SimpleMarker").createSymbolLayer( symbol_props) # NOTE: Cross symbols rendered for OQ-Engine disaggregation outputs are # opaque, wider and thicker than those used for other outputs (e.g. # hcurves) if self.output_type == 'disagg': cross.setSize(3) cross.setStrokeWidth(0.5) opacity = 1 symbol = QgsSymbol.defaultSymbol(self.layer.geometryType()).clone() symbol.deleteSymbolLayer(0) symbol.appendSymbolLayer(cross) renderer = QgsSingleSymbolRenderer(symbol) effect = QgsOuterGlowEffect() effect.setSpread(0.5) effect.setOpacity(opacity) effect.setColor(QColor(255, 255, 255)) effect.setBlurLevel(1) renderer.paintEffect().appendEffect(effect) renderer.paintEffect().setEnabled(True) self.layer.setRenderer(renderer) self.layer.setOpacity(opacity) self.layer.triggerRepaint() # NOTE QGIS3: probably not needed # self.iface.layerTreeView().refreshLayerSymbology(self.layer.id()) self.iface.mapCanvas().refresh() def open_load_zonal_layer_dialog(self): """ Open a file dialog to select the zonal layer to be loaded :returns: the zonal layer """ text = self.tr('Select zonal layer to import') filters = self.tr('All files (*.*);;' 'GeoPackages (*.gpkg);;' 'Vector shapefiles (*.shp);;' 'SQLite (*.sqlite);;') default_dir = QSettings().value('irmt/select_layer_dir', QDir.homePath()) file_name, _ = QFileDialog.getOpenFileName(self, text, default_dir, filters) if not file_name: return None selected_dir = QFileInfo(file_name).dir().path() QSettings().setValue('irmt/select_layer_dir', selected_dir) zonal_layer = self.load_zonal_layer(file_name) return zonal_layer def load_zonal_layer(self, zonal_layer_path): self.added_zonal_layer = zonal_layer = None zonal_layer_basename, zonal_layer_ext = os.path.splitext( os.path.basename(zonal_layer_path)) if zonal_layer_ext == '.gpkg': dlg = QgsSublayersDialog(QgsSublayersDialog.Ogr, 'Select zonal layer') conn = ogr.Open(zonal_layer_path) layer_defs = [] for idx, c in enumerate(conn): ld = QgsSublayersDialog.LayerDefinition() ld.layerId = idx ld.layerName = c.GetDescription() ld.count = c.GetFeatureCount() ld.type = ogr.GeometryTypeToName(c.GetGeomType()) layer_defs.append(ld) dlg.populateLayerTable(layer_defs) dlg.exec_() if not dlg.selection(): return None for sel in dlg.selection(): # NOTE: the last one will be chosen as zonal layer zonal_layer = QgsVectorLayer( zonal_layer_path + "|layername=" + sel.layerName, sel.layerName, 'ogr') if zonal_layer.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer, False) root.insertLayer(0, zonal_layer) else: msg = 'Invalid layer' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None else: zonal_layer = QgsVectorLayer(zonal_layer_path, zonal_layer_basename, 'ogr') if not zonal_layer.geometryType() == QgsWkbTypes.PolygonGeometry: msg = 'Zonal layer must contain zone polygons' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None if zonal_layer_ext != '.gpkg': # Add zonal layer to registry if zonal_layer.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer, False) root.insertLayer(0, zonal_layer) else: msg = 'Invalid zonal layer' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None self.added_zonal_layer = zonal_layer self.pre_populate_zonal_layer_cbx() return zonal_layer def populate_zonal_layer_cbx(self, zonal_layer): cbx = self.zonal_layer_cbx cbx.addItem(zonal_layer.name()) last_index = cbx.count() - 1 cbx.setItemData(last_index, zonal_layer.id()) cbx.setCurrentIndex(last_index) def show_file_size(self): file_size = get_file_size(self.path) self.file_size_lbl.setText(self.file_size_msg % file_size) def accept(self): log_msg('Loading output started. Watch progress in QGIS task bar', level='I', message_bar=self.iface.messageBar()) try: self.iface.layerTreeView().currentLayerChanged.disconnect( self.on_currentLayerChanged) except Exception: # it's connected only for some loaders pass self.hide() if self.output_type in OQ_EXTRACT_TO_LAYER_TYPES: self.load_from_npz() if self.output_type in ('avg_losses-rlzs', 'damages-rlzs', 'avg_losses-stats'): # check if also aggregating by zone or not if (not self.zonal_layer_cbx.currentText() or not self.zonal_layer_gbx.isChecked()): super().accept() return self.aggregate_by_zone() else: super().accept() elif self.output_type in OQ_CSV_TO_LAYER_TYPES: self.load_from_csv() super().accept() def aggregate_by_zone(self): loss_layer = self.layer zonal_layer_id = self.zonal_layer_cbx.itemData( self.zonal_layer_cbx.currentIndex()) zonal_layer = QgsProject.instance().mapLayer(zonal_layer_id) QgsProject.instance().layerTreeRoot().findLayer( zonal_layer.id()).setItemVisibilityChecked(False) # if the two layers have different projections, display a # warning, but try proceeding anyway have_same_projection, check_projection_msg = ProcessLayer( loss_layer).has_same_projection_as(zonal_layer) if not have_same_projection: log_msg(check_projection_msg, level='W', message_bar=self.iface.messageBar()) try: [self.loss_attr_name ] = [field.name() for field in loss_layer.fields()] except ValueError: self.loss_attr_name = self.default_field_name zonal_layer_plus_sum_name = "%s: %s_sum" % (zonal_layer.name(), self.loss_attr_name) discard_nonmatching = self.discard_nonmatching_chk.isChecked() try: calculate_zonal_stats(self.on_calculate_zonal_stats_completed, zonal_layer, loss_layer, [self.loss_attr_name], zonal_layer_plus_sum_name, discard_nonmatching=discard_nonmatching, predicates=('intersects', ), summaries=('sum', )) except Exception as exc: log_msg(str(exc), level='C', message_bar=self.iface.messageBar(), exception=exc) def on_calculate_zonal_stats_completed(self, zonal_layer_plus_sum): if zonal_layer_plus_sum is None: msg = 'The calculation of zonal statistics was not completed' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None # Add zonal layer to registry if zonal_layer_plus_sum.isValid(): root = QgsProject.instance().layerTreeRoot() QgsProject.instance().addMapLayer(zonal_layer_plus_sum, False) root.insertLayer(0, zonal_layer_plus_sum) else: msg = 'The layer aggregating data by zone is invalid.' log_msg(msg, level='C', message_bar=self.iface.messageBar()) return None # NOTE: in scenario damage, keys are like # u'structural_no_damage_mean', and not just # u'structural', therefore we can't just use the selected # loss type, but we must use the actual only key in the # dict added_loss_attr = "%s_sum" % self.loss_attr_name style_by = added_loss_attr try: perils = self.perils except AttributeError: perils = None self.style_maps(zonal_layer_plus_sum, style_by, self.iface, self.output_type, perils=perils, add_null_class=True) super().accept() def reject(self): try: self.iface.layerTreeView().currentLayerChanged.disconnect( self.on_currentLayerChanged) except Exception: # it's connected only for some loaders pass super().reject()
class ModelerParameterDefinitionDialog(QDialog): PARAMETER_NUMBER = 'Number' PARAMETER_RASTER = 'Raster layer' PARAMETER_TABLE = 'Table' PARAMETER_VECTOR = 'Vector layer' PARAMETER_STRING = 'String' PARAMETER_EXPRESSION = 'Expression' PARAMETER_BOOLEAN = 'Boolean' PARAMETER_TABLE_FIELD = 'Table field' PARAMETER_EXTENT = 'Extent' PARAMETER_FILE = 'File' PARAMETER_POINT = 'Point' PARAMETER_CRS = 'CRS' PARAMETER_MULTIPLE = 'Multiple input' paramTypes = [ PARAMETER_BOOLEAN, PARAMETER_EXTENT, PARAMETER_FILE, PARAMETER_NUMBER, PARAMETER_RASTER, PARAMETER_STRING, PARAMETER_EXPRESSION, PARAMETER_TABLE, PARAMETER_TABLE_FIELD, PARAMETER_VECTOR, PARAMETER_POINT, PARAMETER_CRS, PARAMETER_MULTIPLE ] def __init__(self, alg, paramType=None, param=None): self.alg = alg self.paramType = paramType self.param = param QDialog.__init__(self) self.setModal(True) self.setupUi() def setupUi(self): self.setWindowTitle(self.tr('Parameter definition')) self.setMinimumWidth(300) self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setMargin(20) self.label = QLabel(self.tr('Parameter name')) self.verticalLayout.addWidget(self.label) self.nameTextBox = QLineEdit() self.verticalLayout.addWidget(self.nameTextBox) if isinstance(self.param, Parameter): self.nameTextBox.setText(self.param.description) if self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BOOLEAN or \ isinstance(self.param, ParameterBoolean): self.state = QCheckBox() self.state.setText(self.tr('Checked')) self.state.setChecked(False) if self.param is not None: self.state.setChecked(bool(self.param.value)) self.verticalLayout.addWidget(self.state) elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or \ isinstance(self.param, ParameterTableField): self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() idx = 0 for param in list(self.alg.inputs.values()): if isinstance(param.param, (ParameterVector, ParameterTable)): self.parentCombo.addItem(param.param.description, param.param.name) if self.param is not None: if self.param.parent == param.param.name: self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) # add the datatype selector self.verticalLayout.addWidget(QLabel(self.tr('Allowed data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any'), -1) self.datatypeCombo.addItem(self.tr('Number'), 0) self.datatypeCombo.addItem(self.tr('String'), 1) self.datatypeCombo.addItem(self.tr('Date/time'), 2) self.verticalLayout.addWidget(self.datatypeCombo) if self.param is not None and self.param.datatype is not None: # QComboBoxes indexes start at 0, # self.param.datatype start with -1 that is why I need to do +1 datatypeIndex = self.param.datatype + 1 self.datatypeCombo.setCurrentIndex(datatypeIndex) self.multipleCheck = QCheckBox() self.multipleCheck.setText(self.tr('Accept multiple fields')) self.multipleCheck.setChecked(False) if self.param is not None: self.multipleCheck.setChecked(self.param.multiple) self.verticalLayout.addWidget(self.multipleCheck) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or isinstance(self.param, ParameterVector)): self.verticalLayout.addWidget(QLabel(self.tr('Shape type'))) self.shapetypeCombo = QComboBox() self.shapetypeCombo.addItem(self.tr('Any')) self.shapetypeCombo.addItem(self.tr('Point')) self.shapetypeCombo.addItem(self.tr('Line')) self.shapetypeCombo.addItem(self.tr('Polygon')) if self.param is not None: self.shapetypeCombo.setCurrentIndex(self.param.datatype[0] + 1) self.verticalLayout.addWidget(self.shapetypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, ParameterMultipleInput)): self.verticalLayout.addWidget(QLabel(self.tr('Data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Vector (any)')) self.datatypeCombo.addItem(self.tr('Vector (point)')) self.datatypeCombo.addItem(self.tr('Vector (line)')) self.datatypeCombo.addItem(self.tr('Vector (polygon)')) self.datatypeCombo.addItem(self.tr('Raster')) self.datatypeCombo.addItem(self.tr('File')) if self.param is not None: self.datatypeCombo.setCurrentIndex(self.param.datatype + 1) self.verticalLayout.addWidget(self.datatypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, ParameterNumber)): self.verticalLayout.addWidget(QLabel(self.tr('Min value'))) self.minTextBox = QLineEdit() self.verticalLayout.addWidget(self.minTextBox) self.verticalLayout.addWidget(QLabel(self.tr('Max value'))) self.maxTextBox = QLineEdit() self.verticalLayout.addWidget(self.maxTextBox) if self.param is not None: self.minTextBox.setText(str(self.param.min)) self.maxTextBox.setText(str(self.param.max)) self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() self.defaultTextBox.setText(self.tr('0')) if self.param is not None: default = self.param.default if self.param.isInteger: default = int(math.floor(default)) if default: self.defaultTextBox.setText(str(default)) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXPRESSION or isinstance(self.param, ParameterExpression)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultEdit = QgsExpressionLineEdit() if self.param is not None: self.defaultEdit.setExpression(self.param.default) self.verticalLayout.addWidget(self.defaultEdit) self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() self.parentCombo.addItem(self.tr("None"), None) idx = 1 for param in list(self.alg.inputs.values()): if isinstance(param.param, (ParameterVector, ParameterTable)): self.parentCombo.addItem(param.param.description, param.param.name) if self.param is not None: if self.param.parent_layer == param.param.name: self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or isinstance(self.param, ParameterString)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() if self.param is not None: self.defaultTextBox.setText(self.param.default) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or isinstance(self.param, ParameterFile)): self.verticalLayout.addWidget(QLabel(self.tr('Type'))) self.fileFolderCombo = QComboBox() self.fileFolderCombo.addItem(self.tr('File')) self.fileFolderCombo.addItem(self.tr('Folder')) if self.param is not None: self.fileFolderCombo.setCurrentIndex( 1 if self.param.isFolder else 0) self.verticalLayout.addWidget(self.fileFolderCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_POINT or isinstance(self.param, ParameterPoint)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() if self.param is not None: self.defaultTextBox.setText(self.param.default) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_CRS or isinstance(self.param, ParameterCrs)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.selector = QgsProjectionSelectionWidget() if self.param is not None: self.selector.setCrs( QgsCoordinateReferenceSystem(self.param.default)) else: self.selector.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) self.verticalLayout.addWidget(self.selector) self.verticalLayout.addSpacing(20) self.requiredCheck = QCheckBox() self.requiredCheck.setText(self.tr('Mandatory')) self.requiredCheck.setChecked(True) if self.param is not None: self.requiredCheck.setChecked(not self.param.optional) self.verticalLayout.addWidget(self.requiredCheck) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.setObjectName('buttonBox') self.buttonBox.accepted.connect(self.okPressed) self.buttonBox.rejected.connect(self.cancelPressed) self.verticalLayout.addStretch() self.verticalLayout.addWidget(self.buttonBox) self.setLayout(self.verticalLayout) def okPressed(self): description = str(self.nameTextBox.text()) if description.strip() == '': QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Invalid parameter name')) return if self.param is None: validChars = \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' safeName = ''.join(c for c in description if c in validChars) name = safeName.lower() i = 2 while name in self.alg.inputs: name = safeName.lower() + str(i) else: name = self.param.name if (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BOOLEAN or isinstance(self.param, ParameterBoolean)): self.param = ParameterBoolean(name, description, self.state.isChecked()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or isinstance(self.param, ParameterTableField)): if self.parentCombo.currentIndex() < 0: QMessageBox.warning( self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() datatype = self.datatypeCombo.currentData() self.param = ParameterTableField( name, description, parent, datatype, multiple=self.multipleCheck.isChecked()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or isinstance(self.param, ParameterRaster)): self.param = ParameterRaster(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE or isinstance(self.param, ParameterTable)): self.param = ParameterTable(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or isinstance(self.param, ParameterVector)): self.param = ParameterVector( name, description, [self.shapetypeCombo.currentIndex() - 1]) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, ParameterMultipleInput)): self.param = ParameterMultipleInput( name, description, self.datatypeCombo.currentIndex() - 1) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, ParameterNumber)): try: vmin = self.minTextBox.text().strip() if vmin == '': vmin = None else: vmin = float(vmin) vmax = self.maxTextBox.text().strip() if vmax == '': vmax = None else: vmax = float(vmax) self.param = ParameterNumber(name, description, vmin, vmax, str(self.defaultTextBox.text())) except: QMessageBox.warning( self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXPRESSION or isinstance(self.param, ParameterExpression)): parent = self.parentCombo.currentData() self.param = ParameterExpression( name, description, default=str(self.defaultEdit.expression()), parent_layer=parent) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or isinstance(self.param, ParameterString)): self.param = ParameterString(name, description, str(self.defaultTextBox.text())) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXTENT or isinstance(self.param, ParameterExtent)): self.param = ParameterExtent(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or isinstance(self.param, ParameterFile)): isFolder = self.fileFolderCombo.currentIndex() == 1 self.param = ParameterFile(name, description, isFolder=isFolder) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_POINT or isinstance(self.param, ParameterPoint)): self.param = ParameterPoint(name, description, str(self.defaultTextBox.text())) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_CRS or isinstance(self.param, ParameterCrs)): self.param = ParameterCrs(name, description, default=self.selector.crs().authid()) self.param.optional = not self.requiredCheck.isChecked() self.close() def cancelPressed(self): self.param = None self.close()
def _setupUI(self): self.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # deselected widget self.deselected_widget = QListWidget(self) self._set_list_widget_defaults(self.deselected_widget) deselected_label = QLabel() deselected_label.setText('Deselected') deselected_label.setAlignment(Qt.AlignCenter) deselected_label.setFont(italic_font) deselected_v_layout = QVBoxLayout() deselected_v_layout.addWidget(deselected_label) deselected_v_layout.addWidget(self.deselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText('Selected') selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton('>>') self.deselect_all_btn = SmallQPushButton('<<') self.select_btn = SmallQPushButton('>') self.deselect_btn = SmallQPushButton('<') self.select_btn.setToolTip('Add the selected items') self.deselect_btn.setToolTip('Remove the selected items') self.select_all_btn.setToolTip('Add all') self.deselect_all_btn.setToolTip('Remove all') # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(deselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout)
def __init__(self, alg): super(AlgorithmDialogBase, self).__init__(iface.mainWindow() if iface else None) self.setupUi(self) # don't collapse parameters panel self.splitter.setCollapsible(0, False) # add collapse button to splitter splitterHandle = self.splitter.handle(1) handleLayout = QVBoxLayout() handleLayout.setContentsMargins(0, 0, 0, 0) self.btnCollapse = QToolButton(splitterHandle) self.btnCollapse.setAutoRaise(True) self.btnCollapse.setFixedSize(12, 12) self.btnCollapse.setCursor(Qt.ArrowCursor) handleLayout.addWidget(self.btnCollapse) handleLayout.addStretch() splitterHandle.setLayout(handleLayout) self.settings = QgsSettings() self.splitter.restoreState(self.settings.value("/Processing/dialogBaseSplitter", QByteArray())) self.restoreGeometry(self.settings.value("/Processing/dialogBase", QByteArray())) self.splitterState = self.splitter.saveState() self.splitterChanged(0, 0) self.executed = False self.mainWidget = None self.alg = alg self.setWindowTitle(self.alg.displayName()) self.buttonBox.rejected.connect(self.reject) self.buttonBox.accepted.connect(self.accept) # Rename OK button to Run self.btnRun = self.buttonBox.button(QDialogButtonBox.Ok) self.btnRun.setText(self.tr('Run')) self.buttonCancel.setEnabled(False) self.btnClose = self.buttonBox.button(QDialogButtonBox.Close) self.buttonBox.helpRequested.connect(self.openHelp) self.btnCollapse.clicked.connect(self.toggleCollapsed) self.splitter.splitterMoved.connect(self.splitterChanged) # desktop = QDesktopWidget() # if desktop.physicalDpiX() > 96: # self.txtHelp.setZoomFactor(desktop.physicalDpiX() / 96) algHelp = self.formatHelp(self.alg) if algHelp is None: self.textShortHelp.hide() else: self.textShortHelp.document().setDefaultStyleSheet('''.summary { margin-left: 10px; margin-right: 10px; } h2 { color: #555555; padding-bottom: 15px; } a { text-decoration: none; color: #3498db; font-weight: bold; } p { color: #666666; } b { color: #333333; } dl dd { margin-bottom: 5px; }''') self.textShortHelp.setHtml(algHelp) def linkClicked(url): webbrowser.open(url.toString()) self.textShortHelp.anchorClicked.connect(linkClicked) self.showDebug = ProcessingConfig.getSetting( ProcessingConfig.SHOW_DEBUG_IN_DIALOG)
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 | +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.parameters: 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.flags() & QgsProcessingParameterDefinition.FlagHidden: 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.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 output in self._alg.outputs: if output.flags() & QgsProcessingParameterDefinition.FlagHidden: 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.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._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.flags() & QgsProcessingParameterDefinition.FlagAdvanced: 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.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if param.name() in alg.params: value = alg.params[param.name()] else: value = param.defaultValue() 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.parameterDefinitions() outputs = self._alg.outputs for param in params: if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.checkValueIsAcceptable( self.wrappers[param.name()].value): 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.flags( ) & QgsProcessingParameterDefinition.FlagHidden: 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 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 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( "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.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 __init__(self, parent, alg): ParametersPanel.__init__( self, parent, alg) ## Création le l'interface Qt pour les paramètres ## Add console command w = QWidget() # New Qt Windows layout = QVBoxLayout() # New Qt vertical Layout layout.setMargin(0) layout.setSpacing(6) label = QLabel() # New Qt label (text) label.setText(self.tr("Chloe/Java console call")) layout.addWidget(label) # Add label in layout self.text = QPlainTextEdit() # New Qt champs de text in/out self.text.setReadOnly(True) # Read only layout.addWidget(self.text) # Add in layout w.setLayout(layout) # layout -in-> Windows self.layoutMain.addWidget(w) # windows -in-> Windows system self.types_of_metrics = {} for output in self.alg.outputs: if isinstance(output, (OutputRaster, OutputVector, OutputTable)): self.checkBoxes[output.name].setText( self.tr('Open output file after running algorithm')) self.connectParameterSignals() self.parametersHaveChanged() self.changeWindowShapeDependent() # === param:WINDOW_SIZES Widget:IntListSelectionPanel === lineEdit = self.widgets["WINDOW_SIZES"].lineEdit lineEdit.setReadOnly(True) sbInt = self.widgets["WINDOW_SIZES"].sbInt sbInt.setValue(3) # === param:METRICS Widget:ListSelectionPanel === # === Init cbFilter cbFilter = self.widgets["METRICS"].cbFilter cbFilter.addItems(self.alg.types_of_metrics.keys()) # === Init listSrc value = cbFilter.currentText() listSrc = self.widgets["METRICS"].listSrc listSrc.clear() if self.types_of_metrics: if value in self.types_of_metrics.keys(): listSrc.addItems(self.types_of_metrics[value]) # === Init listDest listDest = self.widgets["METRICS"].listDest listDest.clear() self.metrics_selected = set() self.window_sizes_selected = set() lineEdit = self.widgets["METRICS"].lineEdit lineEdit.setReadOnly(True)
class AboutDialog(QDialog): def __init__(self): QDialog.__init__(self) self.initGui() self.btnHelp = self.buttonBox.button(QDialogButtonBox.Help) self.lblLogo.setPixmap(QPixmap(":/icons/qconsolidate.png")) cfg = configparser.SafeConfigParser() cfg.read(os.path.join(os.path.dirname(__file__), "metadata.txt")) version = cfg.get("general", "version") self.lblVersion.setText(self.tr("Version: %s") % (version)) doc = QTextDocument() doc.setHtml(self.getAboutText()) self.textBrowser.setDocument(doc) self.textBrowser.setOpenExternalLinks(True) self.buttonBox.helpRequested.connect(self.openHelp) self.btnClose = self.buttonBox.button(QDialogButtonBox.Close) self.btnClose.clicked.connect(self.reject) def initGui(self): self.setWindowTitle('QConsolidate3') self.buttonBox = QDialogButtonBox(QDialogButtonBox.Close | QDialogButtonBox.Help) self.label = QLabel("QConsolidate3") self.label.setStyleSheet("font-weight: bold") self.lblLogo = QLabel() self.lblVersion = QLabel() self.textBrowser = QTextBrowser() self.h_layout = QHBoxLayout() self.h_layout.addWidget(self.lblLogo) self.h_layout.addWidget(self.label) self.v_layout = QVBoxLayout() self.v_layout.addLayout(self.h_layout) self.v_layout.addWidget(self.lblVersion) self.v_layout.addWidget(self.textBrowser) self.v_layout.addWidget(self.buttonBox) self.setLayout(self.v_layout) def openHelp(self): QDesktopServices.openUrl( QUrl("https://github.com/danzig666/qconsolidate3")) def getAboutText(self): return self.tr(""" <p>Consolidates all layers from current QGIS project into one directory (optionally zipping the whole project in a single file).</p> <p><strong>Developed by</strong>: Danzig </p> <p>Fork of the q-consolidate plugin by Alexander Bruy</p> <p>and the OQ-Consolidate plugin GEM Foundation</p> <p><strong>Homepage</strong>: <a href="https://github.com/danzig666/qconsolidate3/"> homepage</a></p> <p>Please report bugs at <a href="https://github.com/danzig666/qconsolidate3/issues"> bugtracker</a>.</p> """)
def AddFileRowToManager(self, name, filename, load_id=None, islocal=False, klv_folder=None): ''' Add file Video to new Row ''' # We limit the number of videos due to the buffer if self.VManager.rowCount() > 5: qgsu.showUserAndLogMessage( QCoreApplication.translate( "ManagerDock", "You must delete some video from the list before adding a new one" )) return self.islocal = islocal self.klv_folder = klv_folder self.isStreaming = False w = QWidget() layout = QVBoxLayout() pbar = QProgressBar() layout.addWidget(pbar) w.setLayout(layout) rowPosition = self.VManager.rowCount() pbar.setGeometry(0, 0, 300, 30) pbar.setValue(0) pbar.setMaximumHeight(30) if load_id is None: row_id = 0 if rowPosition != 0: row_id = int(self.VManager.item(rowPosition - 1, 0).text()) + 1 else: row_id = load_id self.VManager.insertRow(rowPosition) self.VManager.setItem(rowPosition, 0, QTableWidgetItem(str(row_id))) self.VManager.setItem(rowPosition, 1, QTableWidgetItem(name)) self.VManager.setItem( rowPosition, 2, QTableWidgetItem( QCoreApplication.translate("ManagerDock", "Loading"))) self.VManager.setItem(rowPosition, 3, QTableWidgetItem(filename)) self.VManager.setItem(rowPosition, 4, QTableWidgetItem("-")) self.VManager.setCellWidget(rowPosition, 5, w) self.VManager.setVisible(False) self.VManager.horizontalHeader().setStretchLastSection(True) self.VManager.setVisible(True) # resolve if it is a stream if "://" in filename: self.isStreaming = True if not self.isStreaming: # Disable row if not exist video file if not os.path.exists(filename): self.ToggleActiveRow(rowPosition, value="Missing source file") for j in range(self.VManager.columnCount()): try: self.VManager.item(rowPosition, j).setFlags(Qt.NoItemFlags | Qt.ItemIsEnabled) self.VManager.item(rowPosition, j).setBackground( QColor(211, 211, 211)) except Exception: self.VManager.cellWidget(rowPosition, j).setStyleSheet( "background-color:rgb(211,211,211);") pass return pbar.setValue(30) info = FFMpeg().probe(filename) if info is None: qgsu.showUserAndLogMessage( QCoreApplication.translate("ManagerDock", "Failed loading FFMPEG ! ")) return info.format.duration # init non-blocking metadata buffered reader self.meta_reader[str(rowPosition)] = BufferedMetaReader( filename, pass_time=self.pass_time) qgsu.showUserAndLogMessage( "", "buffered non-blocking metadata reader initialized.", onlyLog=True) pbar.setValue(60) try: # init point we can center the video on self.initialPt[str(rowPosition)] = getVideoLocationInfo( filename, islocal, klv_folder) if not self.initialPt[str(rowPosition)]: self.VManager.setItem( rowPosition, 4, QTableWidgetItem( QCoreApplication.translate( "ManagerDock", "Start location not available."))) self.ToggleActiveRow(rowPosition, value="Not MISB") pbar.setValue(99) return else: self.VManager.setItem( rowPosition, 4, QTableWidgetItem(self.initialPt[str(rowPosition)][2])) except Exception: qgsu.showUserAndLogMessage( QCoreApplication.translate( "ManagerDock", "This video don't have Metadata ! ")) pbar.setValue(99) self.ToggleActiveRow(rowPosition, value="Not MISB") return pbar.setValue(90) dtm_path = parser['GENERAL']['DTM_file'] if self.initialPt[str(rowPosition)] and dtm_path != '': try: initElevationModel(self.initialPt[str(rowPosition)][0], self.initialPt[str(rowPosition)][1], dtm_path) qgsu.showUserAndLogMessage("", "Elevation model initialized.", onlyLog=True) except Exception: None else: self.meta_reader[str(rowPosition)] = StreamMetaReader(filename) qgsu.showUserAndLogMessage("", "StreamMetaReader initialized.", onlyLog=True) self.initialPt[str(rowPosition)] = None pbar.setValue(100) if islocal: self.ToggleActiveRow(rowPosition, value="Ready Local") else: self.ToggleActiveRow(rowPosition, value="Ready") # Add video to settings list AddVideoToSettings(str(row_id), filename)
def populate_details(self): self.bundleWidgets = [] permissions = [img[PERMISSIONS] for img in self.images] item_bundles = order_bundles.bundles_for_item_type( self.item_type, permissions=permissions) default = order_bundles.item_default_bundle_name(self.item_type) def _center(obj): hlayout = QHBoxLayout() hlayout.addStretch() hlayout.addWidget(obj) hlayout.addStretch() return hlayout layout = QVBoxLayout() layout.setMargin(0) layout.setSpacing(20) layout.addLayout(_center(QLabel("<b>RECTIFIED ASSETS</b>"))) gridlayout = QGridLayout() gridlayout.setMargin(0) i = 0 for bundle in item_bundles: bundleid = bundle["id"] if bundle["rectification"] == "orthorectified": name = bundle["name"] description = bundle["description"] udm = bundle.get("auxiliaryFiles", "").lower().startswith("udm2") w = PlanetOrderBundleWidget(bundleid, name, description, udm, True) gridlayout.addWidget(w, i // 2, i % 2) w.setSelected(bundleid == default) w.selectionChanged.connect( partial(self._bundle_selection_changed, w)) self.bundleWidgets.append(w) i += 1 layout.addLayout(gridlayout) self.labelUnrectified = QLabel("<b>UNRECTIFIED ASSETS</b>") layout.addLayout(_center(self.labelUnrectified)) self.widgetUnrectified = QWidget() gridlayoutUnrect = QGridLayout() gridlayoutUnrect.setMargin(0) i = 0 for bundle in item_bundles: bundleid = bundle["id"] if bundle["rectification"] != "orthorectified": name = bundle["name"] description = bundle["description"] udm = bundle.get("auxiliaryFiles", "").lower().startswith("udm2") w = PlanetOrderBundleWidget(bundleid, name, description, udm, False) gridlayoutUnrect.addWidget(w, i // 2, i % 2) w.selectionChanged.connect( partial(self._bundle_selection_changed, w)) self.bundleWidgets.append(w) i += 1 self.widgetUnrectified.setLayout(gridlayoutUnrect) layout.addWidget(self.widgetUnrectified) self.labelMore = QLabel('<a href="#">+ Show More</a>') self.labelMore.setOpenExternalLinks(False) self.labelMore.setTextInteractionFlags(Qt.LinksAccessibleByMouse) self.labelMore.linkActivated.connect(self._showMoreClicked) layout.addLayout(_center(self.labelMore)) self.widgetUnrectified.hide() self.labelUnrectified.hide() self.widgetDetails.setLayout(layout)
class SettingsDialog(QDialog, DIALOG_UI): """ Customizable dialog to configure LADM-COL Assistant. It can be created passing a SettingsContext with specific params or it can be instantiated and then set params one by one. """ db_connection_changed = pyqtSignal(DBConnector, bool, str) # dbconn, ladm_col_db, source open_dlg_import_schema = pyqtSignal( Context) # Context for the import schema dialog def __init__(self, conn_manager=None, context=None, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.parent = parent self.logger = Logger() self.conn_manager = conn_manager self.app = AppInterface() self.sbx_tolerance.setMaximum(TOLERANCE_MAX_VALUE) self._valid_document_repository = False # Needs to be True if users want to enable doc repo (using test button) context = context if context else SettingsContext() self.db_source = context.db_source # default db source is COLLECTED_DB_SOURCE self._required_models = context.required_models self._tab_pages_list = context.tab_pages_list self._blocking_mode = context.blocking_mode # Whether the dialog can only be accepted on valid DB connections or not self._action_type = context.action_type # By default "config" self.setWindowTitle(context.title) self._db = None self.init_db_engine = None self.dbs_supported = ConfigDBsSupported() self._open_dlg_import_schema = False # After accepting, if non-valid DB is configured, we can go to import schema self.online_models_radio_button.setEnabled( False) # This option is disabled until we have online models back! 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.txt_service_endpoint.textEdited.connect( self.source_service_endpoint_changed) # For manual changes only 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 = RoleRegistry() self.load_roles() self.cbo_db_engine.currentIndexChanged.connect(self.db_engine_changed) self.rejected.connect(self.close_dialog) self._update_tabs() if context.tip: self.show_tip(context.tip) 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._update_tabs() def set_required_models(self, required_models): self._required_models = required_models def set_blocking_mode(self, block): self._blocking_mode = block def _update_tabs(self): """ Show only those tabs that are listed in tab_pages_list, if any. If it's an empty list, show all tabs. """ if self._tab_pages_list: for i in reversed(range(self.tabWidget.count())): if i not in self._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): """ We start checking the document repository configuration and only allow to continue if we have a valid repo or if the repo won't be used. Then, 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. """ res_doc_repo, msg_doc_repo = self.check_document_repository_before_saving_settings( ) if not res_doc_repo: self.show_message(msg_doc_repo, Qgis.Warning, 0) return # Do not close the dialog 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: if self._blocking_mode: self.show_message(msg, Qgis.Warning) return # Do not close the dialog else: if self._blocking_mode: 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.") if not ladm_col_schema and self._action_type == EnumDbActionType.CONFIG: msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Question) msg_box.setText( QCoreApplication.translate( "SettingsDialog", "No LADM-COL DB has been configured! You'll continue with limited functionality until you configure a LADM-COL DB.\n\nDo you want to go to 'Create LADM-COL structure' dialog?" )) msg_box.setWindowTitle( QCoreApplication.translate("SettingsDialog", "Important")) msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.Ignore) msg_box.setDefaultButton(QMessageBox.Ignore) msg_box.button(QMessageBox.Yes).setText( QCoreApplication.translate("SettingsDialog", "Yes, go to create structure")) msg_box.button(QMessageBox.Ignore).setText( QCoreApplication.translate("SettingsDialog", "No, I'll do it later")) reply = msg_box.exec_() if reply == QMessageBox.Yes: self._open_dlg_import_schema = True # We will open it when we've closed this Settings dialog # If active role is changed (a check and confirmation may be needed), refresh the GUI # Note: Better to leave this check as the last one in the accepted() method. selected_role = self.get_selected_role() if self.roles.get_active_role() != selected_role: b_change_role = True if STSession().is_user_logged(): reply = QMessageBox.question( self.parent, QCoreApplication.translate("SettingsDialog", "Warning"), QCoreApplication.translate( "SettingsDialog", "You have a ST connection opened and you want to change your role.\nIf you confirm that you want to change your role, you'll be logged out from the ST.\n\nDo you really want to change your role?" ), QMessageBox.Yes | QMessageBox.Cancel, QMessageBox.Cancel) if reply == QMessageBox.Yes: STSession().logout() elif reply == QMessageBox.Cancel: # No need to switch back selected role, the Settings Dialog gets it from role registry b_change_role = False if b_change_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 ) # Emits signal that refreshed the plugin for this role self.save_settings(db) QDialog.accept(self) self.close() if self._open_dlg_import_schema: # After Settings dialog has been closed, we could call Import Schema depending on user's answer above self.open_dlg_import_schema.emit(Context()) self.logger.debug( __name__, "Settings dialog emitted a show Import Schema dialog.") def check_document_repository_before_saving_settings(self): # Check if source service is checked (active). If so, check if either status or endpoint changed. If so, # check if self._valid_document_repository is False. If so, we need to test the service and only allow to save # if such test is successful. res, msg = True, '' if self.connection_box.isChecked( ): # The user wants to have the source service enabled initial_config = QSettings().value( 'Asistente-LADM-COL/sources/use_service', DEFAULT_USE_SOURCE_SERVICE_SETTING, bool) initial_endpoint = QSettings().value( 'Asistente-LADM-COL/sources/service_endpoint', DEFAULT_ENDPOINT_SOURCE_SERVICE) # Config changed or Endpoint changed? if initial_config != self.connection_box.isChecked( ) or initial_endpoint.strip() != self.txt_service_endpoint.text( ).strip(): if not self._valid_document_repository: # A test service has not been run, so we need to do it now self.logger.debug( __name__, "The user wants to enable the source service but the 'test service' has not been run on the current URL. Testing it..." ) res_test, msg_test = self.test_service() if not res_test: res = False msg = QCoreApplication.translate( "SettingsDialog", "The source service is not valid, so it cannot be activated! Adjust such configuration before saving settings." ) return res, msg def source_service_endpoint_changed(self, new_text): # Source service endpoint was changed, so a test_service is required to make the valid variable True self._valid_document_repository = False 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, db): 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()) self.app.settings.tolerance = self.sbx_tolerance.value() settings.setValue('Asistente-LADM-COL/quality/use_roads', self.chk_use_roads.isChecked()) settings.setValue( 'Asistente-LADM-COL/models/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) settings.setValue('Asistente-LADM-COL/sources/use_service', self.connection_box.isChecked()) 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) settings.setValue( 'Asistente-LADM-COL/automatic_values/automatic_values_in_batch_mode', self.chk_automatic_values_in_batch_mode.isChecked()) # Changes in automatic namespace, local_id or t_ili_tid 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) current_t_ili_tid_enabled = settings.value( 'Asistente-LADM-COL/automatic_values/t_ili_tid_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()) settings.setValue( 'Asistente-LADM-COL/automatic_values/t_ili_tid_enabled', self.chk_t_ili_tid.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() or \ current_t_ili_tid_enabled != self.chk_t_ili_tid.isChecked(): if db is not None: self.logger.info( __name__, "Automatic values changed in Settings dialog. All LADM-COL layers are being updated..." ) self.app.core.automatic_fields_settings_changed(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) self.sbx_tolerance.setValue(self.app.settings.tolerance) 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', DEFAULT_AUTOMATIC_VALUES_IN_BATCH_MODE, bool)) self.connection_box.setChecked( settings.value('Asistente-LADM-COL/sources/use_service', DEFAULT_USE_SOURCE_SERVICE_SETTING, 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.chk_t_ili_tid.setChecked( settings.value( 'Asistente-LADM-COL/automatic_values/t_ili_tid_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/models/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.app.core.is_source_service_valid( self.txt_service_endpoint.text().strip()) self._valid_document_repository = res # Required to be True if the user wants to enable the source service self.setEnabled(True) self.show_message(msg['text'], msg['level'], 0) return res, msg def test_service_transitional_system(self): self.setEnabled(False) QCoreApplication.processEvents() res, msg = self.app.core.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, duration=10): self.bar.clearWidgets( ) # Remove previous messages before showing a new one self.bar.pushMessage(message, level, duration) def show_tip(self, tip): self.show_message(tip, Qgis.Info, 0) # Don't show counter for the tip message 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): show_plugin_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)
def __init__(self, images, tool_resources=None): super().__init__(parent=iface.mainWindow()) self.setupUi(self) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.layout().addWidget(self.bar) layout = QVBoxLayout() layout.setMargin(0) self.widgetSummaryItems = QWidget() self.widgetSummaryItems.setLayout(layout) self.scrollAreaSummary.setWidget(self.widgetSummaryItems) self._p_client = PlanetClient.getInstance() self.tool_resources = tool_resources self.txtOrderName.textChanged.connect(self._nameChanged) self.btnPlaceOrder.clicked.connect(self._btnPlaceOrderClicked) self.btnPlaceOrderReview.clicked.connect(self._btnPlaceOrderClicked) self.btnContinueName.clicked.connect( lambda: self.stackedWidget.setCurrentIndex(1)) self.btnContinueAssets.clicked.connect( lambda: self.stackedWidget.setCurrentIndex(2)) self.btnBackReview.clicked.connect( lambda: self.stackedWidget.setCurrentIndex(1)) self.btnBackAssets.clicked.connect( lambda: self.stackedWidget.setCurrentIndex(0)) self.labelPageReview.linkActivated.connect(self._pageLabelClicked) self.labelPageAssets.linkActivated.connect(self._pageLabelClicked) self.labelPageName.linkActivated.connect(self._pageLabelClicked) images_dict = defaultdict(list) #thumbnails_dict = defaultdict(list) for img in images: item_type = img['properties']['item_type'] images_dict[item_type].append(img) #thumbnails_dict[item_type].append(thumbnail) widget = QWidget() self._item_type_widgets = {} layout = QVBoxLayout() layout.setMargin(0) for i, item_type in enumerate(images_dict.keys()): w = PlanetOrderItemTypeWidget(item_type, images_dict[item_type]) if i == 0: w.expand() w.selectionChanged.connect(self.selectionChanged) self._item_type_widgets[item_type] = w layout.addWidget(w) layout.addStretch() widget.setLayout(layout) self.scrollArea.setWidget(widget) self.stackedWidget.currentChanged.connect(self._panelChanged) self.stackedWidget.setCurrentIndex(0) self._panelChanged(0) self._nameChanged() self.selectionChanged()
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 installFromZipFile(self, filePath): if not os.path.isfile(filePath): return settings = QgsSettings() settings.setValue(settingsGroup + '/lastZipDirectory', QFileInfo(filePath).absoluteDir().absolutePath()) with zipfile.ZipFile(filePath, 'r') as zf: pluginName = os.path.split(zf.namelist()[0])[0] pluginFileName = os.path.splitext(os.path.basename(filePath))[0] pluginsDirectory = qgis.utils.home_plugin_path if not QDir(pluginsDirectory).exists(): QDir().mkpath(pluginsDirectory) pluginDirectory = QDir.cleanPath( os.path.join(pluginsDirectory, pluginName)) # If the target directory already exists as a link, # remove the link without resolving QFile(pluginDirectory).remove() password = None infoString = None success = False keepTrying = True while keepTrying: try: # Test extraction. If fails, then exception will be raised and no removing occurs unzip(filePath, pluginsDirectory, password) # Removing old plugin files if exist removeDir(pluginDirectory) # Extract new files unzip(filePath, pluginsDirectory, password) keepTrying = False success = True except Exception as e: success = False if 'password' in str(e): infoString = self.tr('Aborted by user') if 'Bad password' in str(e): msg = self.tr( 'Wrong password. Please enter a correct password to the zip file.' ) else: msg = self.tr( 'The zip file is encrypted. Please enter password.' ) # Display a password dialog with QgsPasswordLineEdit dlg = QDialog() dlg.setWindowTitle(self.tr('Enter password')) buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal) buttonBox.rejected.connect(dlg.reject) buttonBox.accepted.connect(dlg.accept) lePass = QgsPasswordLineEdit() layout = QVBoxLayout() layout.addWidget(QLabel(msg)) layout.addWidget(lePass) layout.addWidget(buttonBox) dlg.setLayout(layout) keepTrying = dlg.exec_() password = lePass.text() else: infoString = self.tr( "Failed to unzip the plugin package\n{}.\nProbably it is broken" .format(filePath)) keepTrying = False if success: updateAvailablePlugins() loadPlugin(pluginName) plugins.getAllInstalled() plugins.rebuild() if settings.contains('/PythonPlugins/' + pluginName): if settings.value('/PythonPlugins/' + pluginName, False, bool): startPlugin(pluginName) reloadPlugin(pluginName) else: unloadPlugin(pluginName) loadPlugin(pluginName) else: if startPlugin(pluginName): settings.setValue('/PythonPlugins/' + pluginName, True) self.exportPluginsToManager() msg = "<b>%s</b>" % self.tr("Plugin installed successfully") else: msg = "<b>%s:</b> %s" % (self.tr("Plugin installation failed"), infoString) level = Qgis.Info if success else Qgis.Critical iface.pluginManagerInterface().pushMessage(msg, level)
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 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.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 list(inputs.values()): param = i.param if isinstance(param, paramType): if dataType is not None: if 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 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 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 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) return alg def setParamValue(self, alg, param, wrapper): try: 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 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) 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.webView = QWebView() html = None url = None isText, help = self._alg.help() if help is not None: if isText: html = help else: url = QUrl(help) else: html = self.tr('<h2>Sorry, no help is available for this ' 'algorithm.</h2>') try: if html: self.webView.setHtml(html) elif url: self.webView.load(url) except: self.webView.setHtml( self.tr('<h2>Could not open help file :-( </h2>')) self.tabWidget.addTab(self.webView, '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 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): values = [] inputs = self.model.inputs for i in inputs.values(): param = i.param if isinstance(param, paramType): 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): 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 1) elif isinstance(param, ParameterFixedTable): item = FixedTablePanel(param) elif isinstance(param, ParameterRange): item = RangePanel(param) elif isinstance(param, ParameterMultipleInput): if param.datatype == ParameterMultipleInput.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) else: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) 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, 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 = CrsSelectionPanel(param.default) 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 == ParameterMultipleInput.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) else: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) 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]): 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 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): authid = widget.getValue() if authid is None and not param.optional: return False alg.params[param.name] = authid return True 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): return self.setParamTableFieldValue(alg, param, widget) elif isinstance(param, ParameterMultipleInput): if param.datatype == ParameterMultipleInput.TYPE_VECTOR_ANY: options = self.getAvailableValuesOfType( ParameterVector, OutputVector) else: options = self.getAvailableValuesOfType( ParameterRaster, OutputRaster) 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() else: QMessageBox.warning(self, self.tr('Unable to add algorithm'), self.tr('Wrong or missing parameter values')) def cancelPressed(self): self.alg = None self.close()
class ModelerParameterDefinitionDialog(QDialog): PARAMETER_NUMBER = 'Number' PARAMETER_RASTER = 'Raster Layer' PARAMETER_TABLE = 'Vector Layer' PARAMETER_VECTOR = 'Feature Source' PARAMETER_STRING = 'String' PARAMETER_EXPRESSION = 'Expression' PARAMETER_BOOLEAN = 'Boolean' PARAMETER_TABLE_FIELD = 'Layer Field' PARAMETER_EXTENT = 'Extent' PARAMETER_FILE = 'File' PARAMETER_POINT = 'Point' PARAMETER_CRS = 'CRS' PARAMETER_MULTIPLE = 'Multiple Input' PARAMETER_BAND = 'Raster Band' PARAMETER_MAP_LAYER = 'Map Layer' paramTypes = [ PARAMETER_BOOLEAN, PARAMETER_EXTENT, PARAMETER_FILE, PARAMETER_NUMBER, PARAMETER_RASTER, PARAMETER_STRING, PARAMETER_EXPRESSION, PARAMETER_MAP_LAYER, PARAMETER_TABLE, PARAMETER_TABLE_FIELD, PARAMETER_VECTOR, PARAMETER_POINT, PARAMETER_CRS, PARAMETER_MULTIPLE, PARAMETER_BAND ] def __init__(self, alg, paramType=None, param=None): self.alg = alg self.paramType = paramType self.param = param QDialog.__init__(self) self.setModal(True) self.setupUi() settings = QgsSettings() self.restoreGeometry( settings.value( "/Processing/modelParametersDefinitionDialogGeometry", QByteArray())) def closeEvent(self, event): settings = QgsSettings() settings.setValue( "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) super(ModelerParameterDefinitionDialog, self).closeEvent(event) def setupUi(self): self.setWindowTitle(self.tr('Parameter definition')) self.setMinimumWidth(300) self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setMargin(20) self.label = QLabel(self.tr('Parameter name')) self.verticalLayout.addWidget(self.label) self.nameTextBox = QLineEdit() self.verticalLayout.addWidget(self.nameTextBox) if isinstance(self.param, QgsProcessingParameterDefinition): self.nameTextBox.setText(self.param.description()) if self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BOOLEAN or \ isinstance(self.param, QgsProcessingParameterBoolean): self.state = QCheckBox() self.state.setText(self.tr('Checked')) self.state.setChecked(False) if self.param is not None: self.state.setChecked(bool(self.param.defaultValue())) self.verticalLayout.addWidget(self.state) elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or \ isinstance(self.param, QgsProcessingParameterField): self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() idx = 0 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition( param.parameterName()) if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentLayerParameterName( ) == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) # add the datatype selector self.verticalLayout.addWidget(QLabel(self.tr('Allowed data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any'), -1) self.datatypeCombo.addItem(self.tr('Number'), 0) self.datatypeCombo.addItem(self.tr('String'), 1) self.datatypeCombo.addItem(self.tr('Date/time'), 2) self.verticalLayout.addWidget(self.datatypeCombo) if self.param is not None and self.param.dataType() is not None: # QComboBoxes indexes start at 0, # self.param.datatype start with -1 that is why I need to do +1 datatypeIndex = self.param.dataType() + 1 self.datatypeCombo.setCurrentIndex(datatypeIndex) self.multipleCheck = QCheckBox() self.multipleCheck.setText(self.tr('Accept multiple fields')) self.multipleCheck.setChecked(False) if self.param is not None: self.multipleCheck.setChecked(self.param.allowMultiple()) self.verticalLayout.addWidget(self.multipleCheck) self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() self.defaultTextBox.setToolTip( self. tr('Default field name, or ; separated list of field names for multiple field parameters' )) if self.param is not None: default = self.param.defaultValue() if default is not None: self.defaultTextBox.setText(str(default)) self.verticalLayout.addWidget(self.defaultTextBox) elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BAND or \ isinstance(self.param, QgsProcessingParameterBand): self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() idx = 0 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition( param.parameterName()) if isinstance(definition, (QgsProcessingParameterRasterLayer)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentLayerParameterName( ) == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) elif (self.paramType in (ModelerParameterDefinitionDialog.PARAMETER_VECTOR, ModelerParameterDefinitionDialog.PARAMETER_TABLE) or isinstance(self.param, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer))): self.verticalLayout.addWidget(QLabel(self.tr('Geometry type'))) self.shapetypeCombo = QComboBox() self.shapetypeCombo.addItem(self.tr('Geometry Not Required'), QgsProcessing.TypeVector) self.shapetypeCombo.addItem(self.tr('Point'), QgsProcessing.TypeVectorPoint) self.shapetypeCombo.addItem(self.tr('Line'), QgsProcessing.TypeVectorLine) self.shapetypeCombo.addItem(self.tr('Polygon'), QgsProcessing.TypeVectorPolygon) self.shapetypeCombo.addItem(self.tr('Any Geometry Type'), QgsProcessing.TypeVectorAnyGeometry) if self.param is not None: self.shapetypeCombo.setCurrentIndex( self.shapetypeCombo.findData(self.param.dataTypes()[0])) self.verticalLayout.addWidget(self.shapetypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.verticalLayout.addWidget(QLabel(self.tr('Data type'))) self.datatypeCombo = QComboBox() self.datatypeCombo.addItem(self.tr('Any Map Layer'), QgsProcessing.TypeMapLayer) self.datatypeCombo.addItem( self.tr('Vector (No Geometry Required)'), QgsProcessing.TypeVector) self.datatypeCombo.addItem(self.tr('Vector (Point)'), QgsProcessing.TypeVectorPoint) self.datatypeCombo.addItem(self.tr('Vector (Line)'), QgsProcessing.TypeVectorLine) self.datatypeCombo.addItem(self.tr('Vector (Polygon)'), QgsProcessing.TypeVectorPolygon) self.datatypeCombo.addItem(self.tr('Vector (Any Geometry Type)'), QgsProcessing.TypeVectorAnyGeometry) self.datatypeCombo.addItem(self.tr('Raster'), QgsProcessing.TypeRaster) self.datatypeCombo.addItem(self.tr('File'), QgsProcessing.TypeFile) if self.param is not None: self.datatypeCombo.setCurrentIndex( self.datatypeCombo.findData(self.param.layerType())) self.verticalLayout.addWidget(self.datatypeCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, QgsProcessingParameterNumber)): self.verticalLayout.addWidget(QLabel(self.tr('Min value'))) self.minTextBox = QLineEdit() self.verticalLayout.addWidget(self.minTextBox) self.verticalLayout.addWidget(QLabel(self.tr('Max value'))) self.maxTextBox = QLineEdit() self.verticalLayout.addWidget(self.maxTextBox) if self.param is not None: self.minTextBox.setText(str(self.param.minimum())) self.maxTextBox.setText(str(self.param.maximum())) self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() self.defaultTextBox.setText(self.tr('0')) if self.param is not None: default = self.param.defaultValue() if self.param.dataType( ) == QgsProcessingParameterNumber.Integer: default = int(math.floor(default)) if default: self.defaultTextBox.setText(str(default)) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXPRESSION or isinstance(self.param, QgsProcessingParameterExpression)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultEdit = QgsExpressionLineEdit() if self.param is not None: self.defaultEdit.setExpression(self.param.defaultValue()) self.verticalLayout.addWidget(self.defaultEdit) self.verticalLayout.addWidget(QLabel(self.tr('Parent layer'))) self.parentCombo = QComboBox() self.parentCombo.addItem(self.tr("None"), None) idx = 1 for param in list(self.alg.parameterComponents().values()): definition = self.alg.parameterDefinition( param.parameterName()) if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)): self.parentCombo.addItem(definition.description(), definition.name()) if self.param is not None: if self.param.parentLayerParameterName( ) == definition.name(): self.parentCombo.setCurrentIndex(idx) idx += 1 self.verticalLayout.addWidget(self.parentCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or isinstance(self.param, QgsProcessingParameterString)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() if self.param is not None: self.defaultTextBox.setText(self.param.defaultValue()) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or isinstance(self.param, QgsProcessingParameterFile)): self.verticalLayout.addWidget(QLabel(self.tr('Type'))) self.fileFolderCombo = QComboBox() self.fileFolderCombo.addItem(self.tr('File')) self.fileFolderCombo.addItem(self.tr('Folder')) if self.param is not None: self.fileFolderCombo.setCurrentIndex(1 if self.param.behavior( ) == QgsProcessingParameterFile.Folder else 0) self.verticalLayout.addWidget(self.fileFolderCombo) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_POINT or isinstance(self.param, QgsProcessingParameterPoint)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.defaultTextBox = QLineEdit() if self.param is not None: self.defaultTextBox.setText(self.param.defaultValue()) self.verticalLayout.addWidget(self.defaultTextBox) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_CRS or isinstance(self.param, QgsProcessingParameterCrs)): self.verticalLayout.addWidget(QLabel(self.tr('Default value'))) self.selector = QgsProjectionSelectionWidget() if self.param is not None: self.selector.setCrs( QgsCoordinateReferenceSystem(self.param.defaultValue())) else: self.selector.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) self.verticalLayout.addWidget(self.selector) self.verticalLayout.addSpacing(20) self.requiredCheck = QCheckBox() self.requiredCheck.setText(self.tr('Mandatory')) self.requiredCheck.setChecked(True) if self.param is not None: self.requiredCheck.setChecked( not self.param.flags() & QgsProcessingParameterDefinition.FlagOptional) self.verticalLayout.addWidget(self.requiredCheck) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self.buttonBox.setObjectName('buttonBox') self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.verticalLayout.addStretch() self.verticalLayout.addWidget(self.buttonBox) self.setLayout(self.verticalLayout) def accept(self): description = str(self.nameTextBox.text()) if description.strip() == '': QMessageBox.warning(self, self.tr('Unable to define parameter'), self.tr('Invalid parameter name')) return if self.param is None: validChars = \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' safeName = ''.join(c for c in description if c in validChars) name = safeName.lower() i = 2 while self.alg.parameterDefinition(name): name = safeName.lower() + str(i) i += 1 else: name = self.param.name() if (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BOOLEAN or isinstance(self.param, QgsProcessingParameterBoolean)): self.param = QgsProcessingParameterBoolean(name, description, self.state.isChecked()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE_FIELD or isinstance(self.param, QgsProcessingParameterField)): if self.parentCombo.currentIndex() < 0: QMessageBox.warning( self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() datatype = self.datatypeCombo.currentData() default = self.defaultTextBox.text() if not default: default = None self.param = QgsProcessingParameterField( name, description, defaultValue=default, parentLayerParameterName=parent, type=datatype, allowMultiple=self.multipleCheck.isChecked()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BAND or isinstance(self.param, QgsProcessingParameterBand)): if self.parentCombo.currentIndex() < 0: QMessageBox.warning( self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return parent = self.parentCombo.currentData() self.param = QgsProcessingParameterBand(name, description, None, parent) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MAP_LAYER or isinstance(self.param, QgsProcessingParameterMapLayer)): self.param = QgsProcessingParameterMapLayer(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or isinstance(self.param, QgsProcessingParameterRasterLayer)): self.param = QgsProcessingParameterRasterLayer(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_TABLE or isinstance(self.param, QgsProcessingParameterVectorLayer)): self.param = QgsProcessingParameterVectorLayer( name, description, [self.shapetypeCombo.currentData()]) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or isinstance(self.param, QgsProcessingParameterFeatureSource)): self.param = QgsProcessingParameterFeatureSource( name, description, [self.shapetypeCombo.currentData()]) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_MULTIPLE or isinstance(self.param, QgsProcessingParameterMultipleLayers)): self.param = QgsProcessingParameterMultipleLayers( name, description, self.datatypeCombo.currentData()) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_NUMBER or isinstance(self.param, QgsProcessingParameterNumber)): try: self.param = QgsProcessingParameterNumber( name, description, QgsProcessingParameterNumber.Double, self.defaultTextBox.text()) vmin = self.minTextBox.text().strip() if not vmin == '': self.param.setMinimum(float(vmin)) vmax = self.maxTextBox.text().strip() if not vmax == '': self.param.setMaximum(float(vmax)) except: QMessageBox.warning( self, self.tr('Unable to define parameter'), self.tr('Wrong or missing parameter values')) return elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXPRESSION or isinstance(self.param, QgsProcessingParameterExpression)): parent = self.parentCombo.currentData() self.param = QgsProcessingParameterExpression( name, description, str(self.defaultEdit.expression()), parent) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_STRING or isinstance(self.param, QgsProcessingParameterString)): self.param = QgsProcessingParameterString( name, description, str(self.defaultTextBox.text())) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_EXTENT or isinstance(self.param, QgsProcessingParameterExtent)): self.param = QgsProcessingParameterExtent(name, description) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_FILE or isinstance(self.param, QgsProcessingParameterFile)): isFolder = self.fileFolderCombo.currentIndex() == 1 self.param = QgsProcessingParameterFile( name, description, QgsProcessingParameterFile.Folder if isFolder else QgsProcessingParameterFile.File) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_POINT or isinstance(self.param, QgsProcessingParameterPoint)): self.param = QgsProcessingParameterPoint( name, description, str(self.defaultTextBox.text())) elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_CRS or isinstance(self.param, QgsProcessingParameterCrs)): self.param = QgsProcessingParameterCrs( name, description, self.selector.crs().authid()) if not self.requiredCheck.isChecked(): self.param.setFlags( self.param.flags() | QgsProcessingParameterDefinition.FlagOptional) settings = QgsSettings() settings.setValue( "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) QDialog.accept(self) def reject(self): self.param = None settings = QgsSettings() settings.setValue( "/Processing/modelParametersDefinitionDialogGeometry", self.saveGeometry()) QDialog.reject(self)
def set_layout(self): vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.toolbar) self.setLayout(vbox)
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 QgsProcessingModelAlgorithm self._alg = alg # 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, (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()) 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=QgsMessageBar.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=QgsMessageBar.WARNING) return None alg.addParameterSources(param.name(), 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): 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().id(), self._alg.groupId(), self._alg.name())).toString() if algHelp not in [None, ""]: webbrowser.open(algHelp)
qgs = QgsApplication(argvb, True) qgs.setStyle(QStyleFactory.create("Fusion")) # Load providers qgs.initQgis() # print(qgs.showSettings()) # print(qgs.libraryPaths()) # print(QgsProviderRegistry.instance().pluginList()) # wrap in dialog dlg = QDialog() layout = QVBoxLayout(dlg) image_lbl = QLabel(dlg) layout.addWidget(image_lbl) # layout.setMargin(0) # First WMS, with tileset image = render_wms_to_image(xyz=False) if image.isNull(): qgs.exitQgis() sys.exit(1) image_lbl.setPixmap(QPixmap.fromImage(image)) dlg.setWindowTitle('WMS Scene Render Test') dlg.exec_() # Then XYZ image = render_wms_to_image(xyz=True)
class OptionsDialog(QDialog, FORM_CLASS): """Options dialog for the InaSAFE plugin.""" def __init__(self, iface, parent=None, qsetting=''): """Constructor for the dialog. :param iface: A Quantum GIS QgisAppInterface instance. :type iface: QgisAppInterface :param parent: Parent widget of this dialog :type parent: QWidget :param qsetting: String to specify the QSettings. By default, use empty string. :type qsetting: str """ QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'configure-inasafe.svg') self.setWindowIcon(QIcon(icon)) self.setWindowTitle(self.tr('InaSAFE %s Options' % get_version())) # Save reference to the QGIS interface and parent self.iface = iface self.parent = parent if qsetting: self.settings = QSettings(qsetting) else: self.settings = QSettings() # InaSAFE default values self.default_value_parameters = [] self.default_value_parameter_containers = [] # Flag for restore default values self.is_restore_default = False # List of setting key and control self.boolean_settings = { 'visibleLayersOnlyFlag': self.cbxVisibleLayersOnly, 'set_layer_from_title_flag': self.cbxSetLayerNameFromTitle, 'setZoomToImpactFlag': self.cbxZoomToImpact, 'set_show_only_impact_on_report': self.cbx_show_only_impact, 'print_atlas_report': self.cbx_print_atlas_report, 'setHideExposureFlag': self.cbxHideExposure, 'useSelectedFeaturesOnly': self.cbxUseSelectedFeaturesOnly, 'useSentry': self.cbxUseSentry, 'template_warning_verbose': self.template_warning_checkbox, 'showOrganisationLogoInDockFlag': self.organisation_on_dock_checkbox, 'developer_mode': self.cbxDevMode, 'generate_report': self.checkbox_generate_reports, 'memory_profile': self.check_box_memory, 'always_show_welcome_message': self.welcome_message_check_box } self.text_settings = { 'keywordCachePath': self.leKeywordCachePath, 'ISO19115_ORGANIZATION': self.organisation_line_edit, 'ISO19115_URL': self.website_line_edit, 'ISO19115_EMAIL': self.email_line_edit, 'ISO19115_LICENSE': self.license_line_edit, } # Export and Import button # Export button self.export_button = QPushButton(tr('Export')) # noinspection PyUnresolvedReferences self.export_button.clicked.connect(self.export_setting) self.button_box.addButton(self.export_button, QDialogButtonBox.ActionRole) # Import button self.import_button = QPushButton(tr('Import')) # noinspection PyUnresolvedReferences self.import_button.clicked.connect(self.import_setting) self.button_box.addButton(self.import_button, QDialogButtonBox.ActionRole) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Always set first tab to be open, 0-th index self.tabWidget.setCurrentIndex(0) # Hide not implemented group self.grpNotImplemented.hide() self.adjustSize() # Population parameter Tab # Label self.preference_label = QLabel() self.preference_label.setText( tr('Please set parameters for each hazard class below. Affected ' 'status and displacement rates selected on this tab are only ' 'applied to exposed populations. ')) self.preference_layout.addWidget(self.preference_label) # Profile preference widget self.profile_widget = ProfileWidget() self.preference_layout.addWidget(self.profile_widget) # Demographic tab self.demographic_label = QLabel() self.demographic_label.setText( tr('Please set the global default demographic ratio below.')) self.default_values_layout.addWidget(self.demographic_label) self.scroll_area = QScrollArea() self.scroll_area.setWidgetResizable(True) self.widget_container = QWidget() self.scroll_area.setWidget(self.widget_container) self.container_layout = QVBoxLayout() self.widget_container.setLayout(self.container_layout) self.default_values_layout.addWidget(self.scroll_area) # Restore state from setting self.restore_state() # Hide checkbox if not developers if not self.cbxDevMode.isChecked(): self.checkbox_generate_reports.hide() # Connections # Check boxes self.custom_north_arrow_checkbox.toggled.connect(self.set_north_arrow) self.custom_UseUserDirectory_checkbox.toggled.connect( self.set_user_dir) self.custom_templates_dir_checkbox.toggled.connect( self.set_templates_dir) self.custom_org_disclaimer_checkbox.toggled.connect( self.set_org_disclaimer) self.custom_organisation_logo_check_box.toggled.connect( self.toggle_logo_path) # Buttons self.toolKeywordCachePath.clicked.connect(self.open_keyword_cache_path) self.toolUserDirectoryPath.clicked.connect( self.open_user_directory_path) self.toolNorthArrowPath.clicked.connect(self.open_north_arrow_path) self.open_organisation_logo_path_button.clicked.connect( self.open_organisation_logo_path) self.toolReportTemplatePath.clicked.connect( self.open_report_template_path) # Others self.organisation_logo_path_line_edit.textChanged.connect( self.update_logo_preview) self.earthquake_function.currentIndexChanged.connect( self.update_earthquake_info) # Set up listener for restore defaults button self.demographic_restore_defaults = self.button_box_restore_defaults.\ button(QDialogButtonBox.RestoreDefaults) self.demographic_restore_defaults.setText( self.demographic_restore_defaults.text().capitalize()) self.demographic_restore_defaults.setCheckable(True) self.demographic_restore_defaults.clicked.connect( self.restore_defaults_ratio) # Restore button in population parameter tab self.parameter_population_restore_button = \ self.button_box_restore_preference.button( QDialogButtonBox.RestoreDefaults) self.parameter_population_restore_button.setText( self.parameter_population_restore_button.text().capitalize()) self.parameter_population_restore_button.clicked.connect( partial(self.restore_population_parameters, global_default=True)) # TODO: Hide this until behaviour is defined # hide template warning toggle self.template_warning_checkbox.hide() # hide custom template dir toggle self.custom_templates_dir_checkbox.hide() self.splitter_custom_report.hide() # Welcome message self.set_welcome_message() def save_boolean_setting(self, key, check_box): """Save boolean setting according to check_box state. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ set_setting(key, check_box.isChecked(), qsettings=self.settings) def restore_boolean_setting(self, key, check_box): """Set check_box according to setting of key. :param key: Key to retrieve setting value. :type key: str :param check_box: Check box to show and set the setting. :type check_box: PyQt5.QtWidgets.QCheckBox.QCheckBox """ flag = setting(key, expected_type=bool, qsettings=self.settings) check_box.setChecked(flag) def save_text_setting(self, key, line_edit): """Save text setting according to line_edit value. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ set_setting(key, line_edit.text(), self.settings) def restore_text_setting(self, key, line_edit): """Set line_edit text according to setting of key. :param key: Key to retrieve setting value. :type key: str :param line_edit: Line edit for user to edit the setting :type line_edit: PyQt5.QtWidgets.QLineEdit.QLineEdit """ value = setting(key, expected_type=str, qsettings=self.settings) line_edit.setText(value) def restore_state(self): """Reinstate the options based on the user's stored session info.""" # Restore boolean setting as check box. for key, check_box in list(self.boolean_settings.items()): self.restore_boolean_setting(key, check_box) # Restore text setting as line edit. for key, line_edit in list(self.text_settings.items()): self.restore_text_setting(key, line_edit) # User Directory user_directory_path = setting(key='defaultUserDirectory', default=temp_dir('impacts'), expected_type=str, qsettings=self.settings) custom_user_directory_flag = (user_directory_path != temp_dir('impacts')) self.custom_UseUserDirectory_checkbox.setChecked( custom_user_directory_flag) self.splitter_user_directory.setEnabled(custom_user_directory_flag) self.leUserDirectoryPath.setText(user_directory_path) # Currency # Populate the currency list for currency in currencies: self.currency_combo_box.addItem(currency['name'], currency['key']) # Then make selected the default one. default_currency = setting('currency', expected_type=str) keys = [currency['key'] for currency in currencies] if default_currency not in keys: default_currency = currencies[0]['key'] index = self.currency_combo_box.findData(default_currency) self.currency_combo_box.setCurrentIndex(index) # Earthquake function. # Populate the combobox first. for model in EARTHQUAKE_FUNCTIONS: self.earthquake_function.addItem(model['name'], model['key']) # Then make selected the default one. default_earthquake_function = setting('earthquake_function', expected_type=str) keys = [model['key'] for model in EARTHQUAKE_FUNCTIONS] if default_earthquake_function not in keys: default_earthquake_function = EARTHQUAKE_FUNCTIONS[0]['key'] index = self.earthquake_function.findData(default_earthquake_function) self.earthquake_function.setCurrentIndex(index) self.update_earthquake_info() # Restore North Arrow Image Path north_arrow_path = setting(key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) custom_north_arrow_flag = (north_arrow_path != default_north_arrow_path()) self.custom_north_arrow_checkbox.setChecked(custom_north_arrow_flag) self.splitter_north_arrow.setEnabled(custom_north_arrow_flag) self.leNorthArrowPath.setText(north_arrow_path) # Restore Report Template Directory Path report_template_directory = setting(key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) custom_templates_dir_flag = (report_template_directory != '') self.custom_templates_dir_checkbox.setChecked( custom_templates_dir_flag) self.leReportTemplatePath.setText(report_template_directory) # Restore Disclaimer org_disclaimer = setting(key='reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) custom_org_disclaimer_flag = (org_disclaimer != disclaimer()) self.custom_org_disclaimer_checkbox.setChecked( custom_org_disclaimer_flag) self.txtDisclaimer.setPlainText(org_disclaimer) # Restore Organisation Logo Path org_logo_path = setting(key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) # Check if the path is default one or not custom_org_logo_flag = org_logo_path != supporters_logo_path() self.organisation_logo_path_line_edit.setText(org_logo_path) self.custom_organisation_logo_check_box.setChecked( custom_org_logo_flag) self.organisation_logo_path_line_edit.setEnabled(custom_org_logo_flag) self.open_organisation_logo_path_button.setEnabled( custom_org_logo_flag) # Manually call here self.update_logo_preview() # Restore InaSAFE default values self.restore_default_values_page() # Restore Population Parameter self.restore_population_parameters(global_default=False) def save_state(self): """Store the options into the user's stored session info.""" # Save boolean settings for key, check_box in list(self.boolean_settings.items()): self.save_boolean_setting(key, check_box) # Save text settings for key, line_edit in list(self.text_settings.items()): self.save_text_setting(key, line_edit) set_setting('north_arrow_path', self.leNorthArrowPath.text(), self.settings) set_setting('organisation_logo_path', self.organisation_logo_path_line_edit.text(), self.settings) set_setting('reportTemplatePath', self.leReportTemplatePath.text(), self.settings) set_setting('reportDisclaimer', self.txtDisclaimer.toPlainText(), self.settings) set_setting('defaultUserDirectory', self.leUserDirectoryPath.text(), self.settings) index = self.earthquake_function.currentIndex() value = self.earthquake_function.itemData(index) set_setting('earthquake_function', value, qsettings=self.settings) currency_index = self.currency_combo_box.currentIndex() currency_key = self.currency_combo_box.itemData(currency_index) set_setting('currency', currency_key, qsettings=self.settings) # Save InaSAFE default values self.save_default_values() # Save population parameters self.save_population_parameters() def accept(self): """Method invoked when OK button is clicked.""" self.save_state() super(OptionsDialog, self).accept() def update_earthquake_info(self): """Update information about earthquake info.""" self.label_earthquake_model() current_index = self.earthquake_function.currentIndex() model = EARTHQUAKE_FUNCTIONS[current_index] notes = '' for note in model['notes']: notes += note + '\n\n' citations = '' for citation in model['citations']: citations += citation['text'] + '\n\n' text = tr( 'Description:\n\n%s\n\n' 'Notes:\n\n%s\n\n' 'Citations:\n\n%s') % (model['description'], notes, citations) self.earthquake_fatality_model_notes.setText(text) def label_earthquake_model(self): model = self.earthquake_function.currentText() help_text = tr( 'Please select your preferred earthquake fatality model. The ' 'default fatality model is the {model}.').format(model=model) self.label_default_earthquake.setText(help_text) def open_keyword_cache_path(self): """Open File dialog to choose the keyword cache path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getSaveFileName( self, self.tr('Set keyword cache file'), self.leKeywordCachePath.text(), self.tr('Sqlite DB File (*.db)')) if file_name: self.leKeywordCachePath.setText(file_name) def open_user_directory_path(self): """Open File dialog to choose the user directory path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Results directory'), self.leUserDirectoryPath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leUserDirectoryPath.setText(directory_name) def open_north_arrow_path(self): """Open File dialog to choose the north arrow path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set north arrow image file'), self.leNorthArrowPath.text(), self.tr('Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.leNorthArrowPath.setText(file_name) def open_organisation_logo_path(self): """Open File dialog to choose the organisation logo path.""" # noinspection PyCallByClass,PyTypeChecker file_name, __ = QFileDialog.getOpenFileName( self, self.tr('Set organisation logo file'), self.organisation_logo_path_line_edit.text(), self.tr('Portable Network Graphics files (*.png *.PNG);;' 'JPEG Images (*.jpg *.jpeg);;' 'GIF Images (*.gif *.GIF);;' 'SVG Images (*.svg *.SVG);;')) if file_name: self.organisation_logo_path_line_edit.setText(file_name) def open_report_template_path(self): """Open File dialog to choose the report template path.""" # noinspection PyCallByClass,PyTypeChecker directory_name = QFileDialog.getExistingDirectory( self, self.tr('Templates directory'), self.leReportTemplatePath.text(), QFileDialog.ShowDirsOnly) if directory_name: self.leReportTemplatePath.setText(directory_name) def toggle_logo_path(self): """Set state of logo path line edit and button.""" is_checked = self.custom_organisation_logo_check_box.isChecked() if is_checked: # Use previous org logo path path = setting(key='organisation_logo_path', default=supporters_logo_path(), expected_type=str, qsettings=self.settings) else: # Set organisation path line edit to default one path = supporters_logo_path() self.organisation_logo_path_line_edit.setText(path) self.organisation_logo_path_line_edit.setEnabled(is_checked) self.open_organisation_logo_path_button.setEnabled(is_checked) def update_logo_preview(self): """Update logo based on the current logo path.""" logo_path = self.organisation_logo_path_line_edit.text() if os.path.exists(logo_path): icon = QPixmap(logo_path) label_size = self.organisation_logo_label.size() label_size.setHeight(label_size.height() - 2) label_size.setWidth(label_size.width() - 2) scaled_icon = icon.scaled(label_size, Qt.KeepAspectRatio) self.organisation_logo_label.setPixmap(scaled_icon) else: self.organisation_logo_label.setText(tr("Logo not found")) def set_north_arrow(self): """Auto-connect slot activated when north arrow checkbox is toggled.""" is_checked = self.custom_north_arrow_checkbox.isChecked() if is_checked: # Show previous north arrow path path = setting(key='north_arrow_path', default=default_north_arrow_path(), expected_type=str, qsettings=self.settings) else: # Set the north arrow line edit to default one path = default_north_arrow_path() self.leNorthArrowPath.setText(path) self.splitter_north_arrow.setEnabled(is_checked) def set_user_dir(self): """Auto-connect slot activated when user dir checkbox is toggled. """ is_checked = self.custom_UseUserDirectory_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting(key='defaultUserDirectory', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = temp_dir('impacts') self.leUserDirectoryPath.setText(path) self.splitter_user_directory.setEnabled(is_checked) def set_templates_dir(self): """Auto-connect slot activated when templates dir checkbox is toggled. """ is_checked = self.custom_templates_dir_checkbox.isChecked() if is_checked: # Show previous templates dir path = setting(key='reportTemplatePath', default='', expected_type=str, qsettings=self.settings) else: # Set the template report dir to '' path = '' self.leReportTemplatePath.setText(path) self.splitter_custom_report.setEnabled(is_checked) def set_org_disclaimer(self): """Auto-connect slot activated when org disclaimer checkbox is toggled. """ is_checked = self.custom_org_disclaimer_checkbox.isChecked() if is_checked: # Show previous organisation disclaimer org_disclaimer = setting('reportDisclaimer', default=disclaimer(), expected_type=str, qsettings=self.settings) else: # Set the organisation disclaimer to the default one org_disclaimer = disclaimer() self.txtDisclaimer.setPlainText(org_disclaimer) self.txtDisclaimer.setEnabled(is_checked) @pyqtSlot(bool) # prevents actions being handled twice def help_toggled(self, flag): """Show or hide the help tab in the stacked widget. .. versionadded: 3.2.1 :param flag: Flag indicating whether help should be shown or hidden. :type flag: bool """ if flag: self.help_button.setText(self.tr('Hide Help')) self.show_help() else: self.help_button.setText(self.tr('Show Help')) self.hide_help() def hide_help(self): """Hide the usage info from the user. .. versionadded: 3.2.1 """ self.main_stacked_widget.setCurrentIndex(1) def show_help(self): """Show usage info to the user.""" # Read the header and footer html snippets self.main_stacked_widget.setCurrentIndex(0) header = html_header() footer = html_footer() string = header message = options_help() string += message.to_html() string += footer self.help_web_view.setHtml(string) def restore_default_values_page(self): """Setup UI for default values setting.""" # Clear parameters so it doesn't add parameters when # restore from changes. if self.default_value_parameters: self.default_value_parameters = [] if self.default_value_parameter_containers: self.default_value_parameter_containers = [] for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) default_fields = all_default_fields() for field_group in all_field_groups: settable_fields = [] for field in field_group['fields']: if field not in default_fields: continue else: settable_fields.append(field) default_fields.remove(field) if not settable_fields: continue # Create group box for each field group group_box = QGroupBox(self) group_box.setTitle(field_group['name']) self.container_layout.addWidget(group_box) parameters = [] for settable_field in settable_fields: parameter = self.default_field_to_parameter(settable_field) if parameter: parameters.append(parameter) parameter_container = ParameterContainer( parameters, description_text=field_group['description'], extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) group_box_inner_layout = QVBoxLayout() group_box_inner_layout.addWidget(parameter_container) group_box.setLayout(group_box_inner_layout) # Add to attribute self.default_value_parameter_containers.append(parameter_container) # Only show non-groups default fields if there is one if len(default_fields) > 0: for default_field in default_fields: parameter = self.default_field_to_parameter(default_field) if parameter: self.default_value_parameters.append(parameter) description_text = tr( 'In this options you can change the global default values for ' 'these variables.') parameter_container = ParameterContainer( self.default_value_parameters, description_text=description_text, extra_parameters=extra_parameter) parameter_container.setup_ui(must_scroll=False) self.other_group_box = QGroupBox(tr('Non-group fields')) other_group_inner_layout = QVBoxLayout() other_group_inner_layout.addWidget(parameter_container) self.other_group_box.setLayout(other_group_inner_layout) self.container_layout.addWidget(self.other_group_box) # Add to attribute self.default_value_parameter_containers.append(parameter_container) def restore_population_parameters(self, global_default=True): """Setup UI for population parameter page from setting. :param global_default: If True, set to original default (from the value in definitions). :type global_default: bool """ if global_default: data = generate_default_profile() else: data = setting('population_preference', generate_default_profile()) if not isinstance(data, dict): LOGGER.debug( 'population parameter is not a dictionary. InaSAFE will use ' 'the default one.') data = generate_default_profile() try: self.profile_widget.data = data except KeyError as e: LOGGER.debug( 'Population parameter is not in correct format. InaSAFE will ' 'use the default one.') LOGGER.debug(e) data = generate_default_profile() self.profile_widget.data = data @staticmethod def age_ratios(): """Helper to get list of age ratio from the options dialog. :returns: List of age ratio. :rtype: list """ # FIXME(IS) set a correct parameter container parameter_container = None youth_ratio = parameter_container.get_parameter_by_guid( youth_ratio_field['key']).value adult_ratio = parameter_container.get_parameter_by_guid( adult_ratio_field['key']).value elderly_ratio = parameter_container.get_parameter_by_guid( elderly_ratio_field['key']).value ratios = [youth_ratio, adult_ratio, elderly_ratio] return ratios def is_good_age_ratios(self): """Method to check the sum of age ratio is 1. :returns: True if the sum is 1 or the sum less than 1 but there is None. :rtype: bool """ ratios = self.age_ratios() if None in ratios: # If there is None, just check to not exceeding 1 clean_ratios = [x for x in ratios if x is not None] ratios.remove(None) if sum(clean_ratios) > 1: return False else: if sum(ratios) != 1: return False return True def save_default_values(self): """Save InaSAFE default values.""" for parameter_container in self.default_value_parameter_containers: parameters = parameter_container.get_parameters() for parameter in parameters: set_inasafe_default_value_qsetting(self.settings, GLOBAL, parameter.guid, parameter.value) def restore_defaults_ratio(self): """Restore InaSAFE default ratio.""" # Set the flag to true because user ask to. self.is_restore_default = True # remove current default ratio for i in reversed(list(range(self.container_layout.count()))): widget = self.container_layout.itemAt(i).widget() if widget is not None: widget.setParent(None) # reload default ratio self.restore_default_values_page() def default_field_to_parameter(self, default_field): """Obtain parameter from default field. :param default_field: A default field definition. :type default_field: dict :returns: A parameter object. :rtype: FloatParameter, IntegerParameter """ if default_field.get('type') == QVariant.Double: parameter = FloatParameter() elif default_field.get('type') in qvariant_whole_numbers: parameter = IntegerParameter() else: return default_value = default_field.get('default_value') if not default_value: message = ('InaSAFE default field %s does not have default value' % default_field.get('name')) LOGGER.exception(message) return parameter.guid = default_field.get('key') parameter.name = default_value.get('name') parameter.is_required = True parameter.precision = default_field.get('precision') parameter.minimum_allowed_value = default_value.get('min_value', 0) parameter.maximum_allowed_value = default_value.get( 'max_value', 100000000) parameter.help_text = default_value.get('help_text') parameter.description = default_value.get('description') # Check if user ask to restore to the most default value. if self.is_restore_default: parameter._value = default_value.get('default_value') else: # Current value qsetting_default_value = get_inasafe_default_value_qsetting( self.settings, GLOBAL, default_field['key']) # To avoid python error if qsetting_default_value > parameter.maximum_allowed_value: qsetting_default_value = parameter.maximum_allowed_value if qsetting_default_value < parameter.minimum_allowed_value: qsetting_default_value = parameter.minimum_allowed_value parameter.value = qsetting_default_value return parameter def save_population_parameters(self): """Helper to save population parameter to QSettings.""" population_parameter = self.profile_widget.data set_setting('population_preference', population_parameter) def set_welcome_message(self): """Create and insert welcome message.""" string = html_header() string += welcome_message().to_html() string += html_footer() self.welcome_message.setHtml(string) def show_option_dialog(self): """Helper to show usual option dialog (without welcome message tab).""" self.tabWidget.removeTab(0) def show_welcome_dialog(self): """Setup for showing welcome message dialog. This method will setup several things: - Only show welcome, organisation profile, and population parameter tab. Currently, they are the first 3 tabs. - Set the title - Move the check box for always showing welcome message. """ self.welcome_layout.addWidget(self.welcome_message_check_box) while self.tabWidget.count() > 3: self.tabWidget.removeTab(self.tabWidget.count() - 1) self.setWindowTitle(self.tr('Welcome to InaSAFE %s' % get_version())) # Hide the export import button self.export_button.hide() self.import_button.hide() def export_setting(self): """Export setting from an existing file.""" LOGGER.debug('Export button clicked') home_directory = os.path.expanduser('~') file_name = self.organisation_line_edit.text().replace(' ', '_') file_path, __ = QFileDialog.getSaveFileName( self, self.tr('Export InaSAFE settings'), os.path.join(home_directory, file_name + '.json'), self.tr('JSON File (*.json)')) if file_path: LOGGER.debug('Exporting to %s' % file_path) export_setting(file_path) def import_setting(self): """Import setting to a file.""" LOGGER.debug('Import button clicked') home_directory = os.path.expanduser('~') file_path, __ = QFileDialog.getOpenFileName( self, self.tr('Import InaSAFE settings'), home_directory, self.tr('JSON File (*.json)')) if file_path: title = tr('Import InaSAFE Settings.') question = tr( 'This action will replace your current InaSAFE settings with ' 'the setting from the file. This action is not reversible. ' 'Are you sure to import InaSAFE Setting?') answer = QMessageBox.question(self, title, question, QMessageBox.Yes | QMessageBox.No) if answer == QMessageBox.Yes: LOGGER.debug('Import from %s' % file_path) import_setting(file_path)