def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.jobs_view.setModel(proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) btn.setMinimumSize(QSize(75, 0)) btn.setMaximumSize(QSize(150, 16777215)) self.jobs_view.setIndexWidget(proxy_model.index(row, 5), btn) #self.jobs_view.widget(proxy_model.index(row, 2)).setTextAlignment(Qt.AlignCenter) # self.jobs_view.setMinimumSize(QSize(0, 100)) # self.jobs_view.setColumnWidth(1, 400) # self.jobs_view.setColumnWidth(2, 200) # self.jobs_view.setColumnWidth(3, 200) # self.jobs_view.setColumnWidth(4, 200) # self.jobs_view.setColumnWidth(5, 200) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed)
def update_jobs_table(self): if self.jobs: table_model = JobsTableModel(self.jobs, self) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(table_model) self.jobs_view.setModel(self.proxy_model) # Add "Details" buttons in cell for row in range(0, len(self.jobs)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.jobs_view.setIndexWidget(self.proxy_model.index(row, 6), btn) self.jobs_view.horizontalHeader().setSectionResizeMode( 0, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 1, QtWidgets.QHeaderView.Stretch) self.jobs_view.horizontalHeader().setSectionResizeMode( 2, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 3, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 4, QtWidgets.QHeaderView.Fixed) self.jobs_view.horizontalHeader().setSectionResizeMode( 5, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.horizontalHeader().setSectionResizeMode( 6, QtWidgets.QHeaderView.ResizeToContents) self.jobs_view.selectionModel().selectionChanged.connect( self.selection_changed)
def __init__(self, parent): """Constructor for the model. :param parent: Parent widget of this model. :type parent: QWidget """ QSortFilterProxyModel.__init__(self, parent)
def __init__(self): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings) # new declaration of ProjectFinder since changes can be cancelled self.project_finder = ProjectFinder(self) # table model self.project_search_model = ProjectSearchModel(self.project_finder) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.project_search_model) self.projectSearchTable.setModel(self.proxyModel) header = self.projectSearchTable.horizontalHeader() # header.setResizeMode(QHeaderView.ResizeToContents) header.setSectionResizeMode(QHeaderView.ResizeToContents) # open/create QuickFinder file self.createFileButton.clicked.connect(self.create_QFTS_file) self.openFileButton.clicked.connect(self.open_QFTS_file) self.read_QFTS_file() # project search self.addSearchButton.clicked.connect(self.add_project_search) self.removeSearchButton.clicked.connect(self.remove_project_search) self.editSearchButton.clicked.connect(self.edit_project_search) self.refreshButton.clicked.connect(self.refresh_project_search) self.projectSearchTable.selectionModel().selectionChanged.connect( self.enableButtons) self.enableButtons()
def setup_class_table(self, classes): default_codes = sorted( [c['Initial_Code'] for c in self.default_classes]) input_codes = sorted([c['Initial_Code'] for c in classes]) new_codes = [c for c in input_codes if c not in default_codes] missing_codes = [c for c in default_codes if c not in input_codes] if len(new_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the definition file do not appear in the chosen data file." .format(', '.join([str(c) for c in new_codes]), None))) if len(missing_codes) > 0: QtWidgets.QMessageBox.warning( None, self.tr("Warning"), self. tr(u"Some of the class codes ({}) in the data file do not appear in the chosen definition file." .format(', '.join([str(c) for c in missing_codes]), None))) # Setup a new classes list with the new class codes for all classes # included in default calsses, and and any other class codes that are # missing added from the default class list classes = [c for c in classes if c['Initial_Code'] in default_codes] classes.extend([ c for c in self.default_classes if c['Initial_Code'] not in input_codes ]) table_model = AggTableModel(classes, parent=self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.remap_view.setModel(proxy_model) # Add selector in cell for row in range(0, len(classes)): lc_classes = QtWidgets.QComboBox() lc_classes.currentIndexChanged.connect(self.lc_class_combo_changed) # Add the classes in order of their codes lc_classes.addItems( sorted(list(self.final_classes.keys()), key=lambda k: self.final_classes[k])) ind = lc_classes.findText(classes[row]['Final_Label']) if ind != -1: lc_classes.setCurrentIndex(ind) self.remap_view.setIndexWidget( proxy_model.index(row, self.remap_view.model().columnCount() - 1), lc_classes) self.remap_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows) self.remap_view.setColumnWidth(1, 450) self.remap_view.horizontalHeader().setStretchLastSection(True) return True
def loadRat(self, band_0_based) -> bool: """Load RAT for raster band 0-based""" if type(band_0_based) != int: rat_log( QCoreApplication.translate( 'RAT', 'Invalid band number for the selected raster.'), Qgis.Critical) return False self.mClassifyComboBox.clear() self.rat = get_rat(self.raster_layer, band_0_based + 1) if self.rat.keys: self.model = RATModel(self.rat) if os.environ.get('CI'): self.tester = QAbstractItemModelTester(self.model) self.model.dataChanged.connect(self.dirty) self.model.rowsInserted.connect(self.dirty) self.model.rowsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.dirty) self.model.columnsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.updateClassify) self.model.columnsRemoved.connect(self.updateClassify) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.mRATView.setModel(self.proxyModel) self.mRATView.selectionModel().selectionChanged.connect( self.updateButtons) # Color picker if self.rat.has_color: if gdal.GFU_Alpha in self.rat.field_usages: colorDelegate = ColorAlphaDelegate(self.mRATView) else: colorDelegate = ColorDelegate(self.mRATView) self.mRATView.setItemDelegateForColumn(0, colorDelegate) self.updateClassify() self.mRATView.sortByColumn( self.model.headers.index(self.rat.value_columns[0]), Qt.AscendingOrder) return True else: rat_log( QCoreApplication.translate( 'RAT', 'There is no Raster Attribute Table for the selected raster.' ), Qgis.Critical) return False
def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet('min-height: 150px') self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[str].connect(self.pFilterModel.setFilterFixedString)
class ExtendedComboBox(QComboBox): def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) self.completer = QCompleter(self.pFilterModel, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.completer.popup().setStyleSheet("min-height: 150px") self.completer.popup().setAlternatingRowColors(True) self.setCompleter(self.completer) self.lineEdit().textEdited[str].connect(self.pFilterModel.setFilterFixedString)
def _initGui(self): """setup the user interface""" self.ui = Ui_geopunt4QgisDataCatalogDlg() self.ui.setupUi(self) # get settings self.s = QSettings() self.loadSettings() self.gh = geometryHelper(self.iface) # setup a message bar self.bar = QgsMessageBar() self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed) self.ui.verticalLayout.addWidget(self.bar) self.ui.buttonBox.addButton(QPushButton("Sluiten"), QDialogButtonBox.RejectRole) for btn in self.ui.buttonBox.buttons(): btn.setAutoDefault(0) # vars self.firstShow = True self.wms = None self.wfs = None self.dl = None self.zoek = '' self.bronnen = None self.model = QStandardItemModel(self) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.ui.resultView.setModel(self.proxyModel) self.completer = QCompleter(self) self.completerModel = QStringListModel(self) self.ui.zoekTxt.setCompleter(self.completer) self.completer.setModel(self.completerModel) # eventhandlers self.ui.zoekBtn.clicked.connect(self.onZoekClicked) self.ui.addWMSbtn.clicked.connect(self.addWMS) self.ui.addWFSbtn.clicked.connect(self.addWFS) self.ui.DLbtn.clicked.connect(lambda: self.openUrl(self.dl)) self.ui.resultView.clicked.connect(self.resultViewClicked) self.ui.modelFilterCbx.currentIndexChanged.connect(self.modelFilterCbxIndexChanged) self.ui.filterWgt.setHidden(1) self.ui.buttonBox.helpRequested.connect(self.openHelp) self.finished.connect(self.clean)
def _update_completer(self, values): # Get the items in a tuple and put them in a list # Store display and actual values in a # model for easier mapping and # retrieval when carrying out searches model_attr_mapping = [] # Check if there are formaters specified # for the current field name for mv in values: f_model_values = [] m_val = mv[0] if m_val is not None: col_label = self.currentFieldName() if col_label in self.config.LookupFormatters: formatter = self.config.LookupFormatters[col_label] if formatter.column.TYPE_INFO == 'LOOKUP': m_val = formatter.code_value(m_val)[0] else: m_val = formatter.format_column_value(m_val) f_model_values.extend([m_val, m_val]) model_attr_mapping.append(f_model_values) self._completer_model = BaseSTDMTableModel(model_attr_mapping, ["", ""], self) # We will use the QSortFilterProxyModel for filtering purposes self._proxy_completer_model = QSortFilterProxyModel() self._proxy_completer_model.setDynamicSortFilter(True) self._proxy_completer_model.setSourceModel(self._completer_model) self._proxy_completer_model.setSortCaseSensitivity(Qt.CaseInsensitive) self._proxy_completer_model.setFilterKeyColumn(0) # Configure completer mod_completer = QCompleter(self._completer_model, self) mod_completer.setCaseSensitivity(Qt.CaseInsensitive) mod_completer.setCompletionMode(QCompleter.PopupCompletion) mod_completer.setCompletionColumn(0) mod_completer.setCompletionRole(Qt.DisplayRole) self.txtFilterPattern.setCompleter(mod_completer)
def __init__(self, parent=None, centeronparent=False, showsave=True): super(BigList, self).__init__(parent) self.setupUi(self) self.centeronparent = centeronparent self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.listView.setWordWrap(True) self.saveButton.setVisible(showsave) utils.install_touch_scroll(self.listView)
def update_data_table(self): table_model = DataTableModel(self.datasets, self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(table_model) self.data_view.setModel(proxy_model) # Add "Notes" buttons in cell for row in range(0, len(self.datasets)): btn = QtWidgets.QPushButton(self.tr("Details")) btn.clicked.connect(self.btn_details) self.data_view.setIndexWidget(proxy_model.index(row, 7), btn) self.data_view.horizontalHeader().setResizeMode( QtWidgets.QHeaderView.ResizeToContents) self.data_view.setSelectionBehavior( QtWidgets.QAbstractItemView.SelectRows)
def config(self, token_model): space_model = XYZSpaceModel(self) proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(space_model) self.tableView_space.setModel(proxy_model) self.tableView_space.setSelectionMode( self.tableView_space.SingleSelection) self.tableView_space.setSelectionBehavior( self.tableView_space.SelectRows) self.tableView_space.setSortingEnabled(True) ############# connect gui self.tableView_space.pressed.connect(self.cb_table_row_selected) self.btn_use.clicked.connect(self._get_space_model().reset) TokenUX.config(self, token_model)
def set_autocompleter(combobox, list_items=None): """ Iterate over the items in the QCombobox, create a list, create the model, and set the model according to the list """ if list_items is None: list_items = [combobox.itemText(i) for i in range(combobox.count())] proxy_model = QSortFilterProxyModel() set_model_by_list(list_items, combobox, proxy_model) combobox.editTextChanged.connect(partial(filter_by_list, combobox, proxy_model))
def __init__(self, parent=None): """Constructor.""" super(NafiDockWidget, self).__init__(parent) self.setupUi(self) # set up QTreeView self.treeView.setHeaderHidden(True) self.treeView.setSortingEnabled(True) self.treeView.setFocusPolicy(Qt.NoFocus) self.treeView.pressed.connect(self.treeViewPressed) # set up search signal self.lineEdit.textChanged.connect(self.searchTextChanged) self.searchText = "" # set up clear search self.clearSearchButton.clicked.connect(self.clearSearch) # set up About … dialog self.aboutButton.clicked.connect(self.showAboutDialog) # set up Download NAFI data … link button self.dataButton.clicked.connect( lambda: webbrowser.open(getNafiDataUrl())) # set up base model self.treeViewModel = NafiTreeViewModel(getNafiUrl()) # set up proxy model for filtering self.proxyModel = QSortFilterProxyModel(self.treeView) self.proxyModel.setSourceModel(self.treeViewModel) self.proxyModel.setRecursiveFilteringEnabled(True) self.treeView.setModel(self.proxyModel) self.reader = NafiCapabilitiesReader() self.reader.capabilitiesDownloaded.connect( lambda xml: self.initModel(xml)) # restore the view from source whenever this dock widget is made visible again self.visibilityChanged.connect( lambda visible: visible and self.loadNafiWms())
class FilteredComboBox(QComboBox): """Custom QComboBox with filtering option.""" def __init__(self, parent=None): super(FilteredComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) self.setEditable(True) self.filter_proxy_model = QSortFilterProxyModel(self) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSourceModel(self.model()) self.completer = QCompleter(self.filter_proxy_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.setMinimumSize(150, 25) self.setFont(QFont("Segoe UI", 10)) self.setStyleSheet("QComboBox {background-color: white;}") self.setMaxVisibleItems(10) self.completer.activated.connect(self.on_completer_activated) self.lineEdit().textEdited.connect( self.filter_proxy_model.setFilterFixedString) def on_completer_activated(self, text): """Set active combobox item when a completer item is picked.""" if not text: return idx = self.findText(text) self.setCurrentIndex(idx) self.activated[str].emit(self.itemText(idx)) def setModel(self, model): """Set completer model after the combobox model.""" super(FilteredComboBox, self).setModel(model) self.filter_proxy_model.setSourceModel(model) self.completer.setModel(self.filter_proxy_model) def setModelColumn(self, column_idx): """Set the correct column for completer and combobox model using column index.""" self.completer.setCompletionColumn(column_idx) self.filter_proxy_model.setFilterKeyColumn(column_idx) super(FilteredComboBox, self).setModelColumn(column_idx)
def __init__(self, parent=None): super(FilteredComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength) self.setEditable(True) self.filter_proxy_model = QSortFilterProxyModel(self) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSortCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setSourceModel(self.model()) self.completer = QCompleter(self.filter_proxy_model, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.setMinimumSize(150, 25) self.setFont(QFont("Segoe UI", 10)) self.setStyleSheet("QComboBox {background-color: white;}") self.setMaxVisibleItems(10) self.completer.activated.connect(self.on_completer_activated) self.lineEdit().textEdited.connect( self.filter_proxy_model.setFilterFixedString)
def __init__(self, parent=None): super(DsgCustomComboBox, self).__init__(parent=parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[str].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated)
def searchModel(self, columnIndex, columnValue): ''' Searches for 'columnValue' in the column whose index is specified by 'columnIndex' in all rows contained in the model. ''' if isinstance(columnValue, QVariant): columnValue = str(columnValue.toString()) if not isinstance(columnValue, str): columnValue = str(columnValue) columnValue = columnValue.strip() proxy = QSortFilterProxyModel(self) proxy.setSourceModel(self._tableModel) proxy.setFilterKeyColumn(columnIndex) proxy.setFilterFixedString(columnValue) # Will return model index containing the primary key. matchingIndex = proxy.mapToSource(proxy.index(0, 0)) return matchingIndex
class BigList(Ui_BigList, QWidget): itemselected = pyqtSignal(QModelIndex) closewidget = pyqtSignal() savewidget = pyqtSignal() def __init__(self, parent=None, centeronparent=False, showsave=True): super(BigList, self).__init__(parent) self.setupUi(self) self.centeronparent = centeronparent self.listView.clicked.connect(self.selected) self.saveButton.pressed.connect(self.savewidget.emit) self.closebutton.pressed.connect(self.closewidget.emit) self._index = None self.search.textEdited.connect(self.set_filter) self.filtermodel = QSortFilterProxyModel() self.filtermodel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.listView.setModel(self.filtermodel) self.listView.setWordWrap(True) self.saveButton.setVisible(showsave) utils.install_touch_scroll(self.listView) def set_filter(self, text): self.filtermodel.setFilterRegExp(text + ".*") def selected(self, index): self._index = index self._index = self.filtermodel.mapToSource(index) self.itemselected.emit(self._index) def setmodel(self, model): self.filtermodel.setSourceModel(model) def setlabel(self, fieldname): self.fieldnameLabel.setText(fieldname) def currentindex(self): return self._index def setcurrentindex(self, index): if index is None: index = QModelIndex() if isinstance(index, int): index = self.listView.model().index(index, 0) self.listView.setCurrentIndex(index) def show(self): super(BigList, self).show() if self.centeronparent: width = self.parent().width() height = self.parent().height() self.move(width / 4, 0) self.resize(QSize(width / 2, height))
def __init__(self, parent): """ Initialise ExtendedCombobox :param parent: Parent of combobox :type parent: PyQt5.QtWidgets.QWidget """ super().__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.p_filter_model = QSortFilterProxyModel(self) self.p_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect(self.p_filter_model.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked)
class ExtendedComboBox(QComboBox): """Extended class of QComboBox so we can perform a filtering of items. """ def __init__(self, parent=None): super(ExtendedComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) # add a filter model to filter matching items self.pFilterModel = QSortFilterProxyModel(self) self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.pFilterModel.setSourceModel(self.model()) # add a completer, which uses the filter model self.completer = QCompleter(self.pFilterModel, self) # always show all (filtered) completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) # connect signals self.lineEdit().textEdited[str].connect( self.pFilterModel.setFilterFixedString) self.completer.activated.connect(self.on_completer_activated) # on selection of an item from the completer, # select the corresponding item from combobox def on_completer_activated(self, text): if text: index = self.findText(text) self.setCurrentIndex(index) # on model change, update the models of the filter and completer as well def setModel(self, model): super(ExtendedComboBox, self).setModel(model) self.pFilterModel.setSourceModel(model) self.completer.setModel(self.pFilterModel) # on model column change, update the model column of # the filter and completer as well def setModelColumn(self, column): self.completer.setCompletionColumn(column) self.pFilterModel.setFilterKeyColumn(column) super(ExtendedComboBox, self).setModelColumn(column)
def set_model_by_list(string_list, widget, proxy_model): """ Set the model according to the list """ model = QStringListModel() model.setStringList(string_list) proxy_model.setSourceModel(model) proxy_model.setFilterKeyColumn(0) proxy_model_aux = QSortFilterProxyModel() proxy_model_aux.setSourceModel(model) proxy_model_aux.setFilterKeyColumn(0) widget.setModel(proxy_model_aux) widget.setModelColumn(0) completer = QCompleter() completer.setModel(proxy_model) completer.setCompletionColumn(0) completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) widget.setCompleter(completer)
class RasterAttributeTableDialog(QDialog): def __init__(self, raster_layer, iface=None): QDialog.__init__(self) # Set up the user interface from Designer. ui_path = os.path.join(os.path.dirname(__file__), 'Ui_RasterAttributeTableDialog.ui') uic.loadUi(ui_path, self) self.raster_layer = raster_layer self.iface = iface self.editable = False self.is_dirty = False self.mRasterBandsComboBox.addItems([ raster_layer.bandName(bn) for bn in range(1, raster_layer.bandCount() + 1) ]) self.mToggleEditingToolButton.setIcon( QgsApplication.getThemeIcon("/mActionEditTable.svg")) self.mAddColumnToolButton.setIcon( QgsApplication.getThemeIcon("/mActionNewAttribute.svg")) self.mAddRowToolButton.setIcon( QgsApplication.getThemeIcon("/mActionNewTableRow.svg")) self.mRemoveRowToolButton.setIcon( QgsApplication.getThemeIcon("/mActionRemoveSelectedFeature.svg")) self.mRemoveColumnToolButton.setIcon( QgsApplication.getThemeIcon("/mActionDeleteAttribute.svg")) self.mSaveChangesToolButton.setIcon( QgsApplication.getThemeIcon("/mActionSaveAllEdits.svg")) stylesheet = "QToolButton {padding: 1px;}" self.mToggleEditingToolButton.setStyleSheet(stylesheet) self.mAddColumnToolButton.setStyleSheet(stylesheet) self.mRemoveColumnToolButton.setStyleSheet(stylesheet) self.mSaveChangesToolButton.setStyleSheet(stylesheet) assert self.loadRat(0) self.setEditable(False) # Connections self.mClassifyButton.clicked.connect(self.classify) self.mRasterBandsComboBox.currentIndexChanged.connect(self.loadRat) self.mButtonBox.accepted.connect(self.accept) self.mButtonBox.rejected.connect(self.reject) self.mToggleEditingToolButton.toggled.connect(self.setEditable) self.mSaveChangesToolButton.clicked.connect(self.saveChanges) self.mAddColumnToolButton.clicked.connect(self.addColumn) self.mRemoveColumnToolButton.clicked.connect(self.removeColumn) self.mAddRowToolButton.clicked.connect(self.addRow) self.mRemoveRowToolButton.clicked.connect(self.removeRow) try: self.restoreGeometry(QgsSettings().value( "RasterAttributeTable/geometry", None, QByteArray, QgsSettings.Plugins)) except: pass self.updateButtons() def addRow(self): current_row = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).row() dlg = AddRowDialog(current_row) if dlg.exec_() == QDialog.Accepted: if not self.model.insert_row( current_row + (0 if dlg.mBefore.isChecked() else 1)): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Row"), QCoreApplication.translate( 'RAT', "An error occourred while adding a new row to the RAT!" )) def removeRow(self): if QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove Row"), QCoreApplication.translate( 'RAT', "Removing a row will remove the value from the RAT, do you want to continue?" )) == QMessageBox.Yes: current_row = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).row() if not self.model.remove_row(current_row): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Removing Row"), QCoreApplication.translate( 'RAT', "An error occourred while removing a row from the RAT!" )) def allowedAddedUsages(self) -> list: """Return the list of not-color usages that can be added :return: allowed usages that can be added :rtype: list """ allowed_usages = [] usages = self.model.rat.field_usages for usage, info in rat_supported_column_info().items(): if not info['is_color'] and (not info['unique'] or usage not in usages): allowed_usages.append(usage) return allowed_usages def canAddAnyColumn(self) -> bool: """Check if any column can be added""" return not self.model.has_color or len(self.allowedAddedUsages()) > 0 def addColumn(self): dlg = AddColumnDialog(self.model, self.iface) if self.model.has_color: dlg.mColumnType.hide() # List columns where insertion is allowed: skip value and count for field in self.model.rat.fields.values(): if field.usage not in {gdal.GFU_MinMax, gdal.GFU_PixelCount}: dlg.mColumn.addItem(field.name) # List allowed usages allowed_usages = self.allowedAddedUsages() # Set insertion point col = self.proxyModel.mapToSource( self.mRATView.selectionModel().currentIndex()).column() header = self.model.headers[col] dlg.mColumn.setCurrentIndex(dlg.mColumn.findText(header)) if not allowed_usages: if not self.model.has_color: dlg.mColor.setChecked(True) dlg.mStandardColumn.setEnabled(False) else: # We cannot add any field, we should never get here! rat_log( 'Cannot add any column: this should have been checked before getting so far!', Qgis.Critical) else: usages_info = rat_column_info() for usage in allowed_usages: dlg.mUsage.addItem(usages_info[usage]['name'], usage) if dlg.exec_() == QDialog.Accepted: position = dlg.mColumn.currentText() after = dlg.mAfter.isChecked() insertion_point = self.model.headers.index(position) + (1 if after else 0) if dlg.mColor.isChecked(): if not self.model.insert_color(insertion_point): QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Colors"), QCoreApplication.translate( 'RAT', "An error occourred while adding colors to the RAT!" )) else: # Try to update colors from current raster self.model.rat.update_colors_from_raster(self.raster_layer) self.is_dirty = True else: data_type = dlg.mDataType.currentData() usage = dlg.mUsage.currentData() name = dlg.mName.text() field = RATField(name, usage, data_type) result, error_message = self.model.insert_column( insertion_point, field) if not result: QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Adding Column"), QCoreApplication.translate( 'RAT', "An error occourred while adding a column to the RAT: %s" % error_message)) else: self.is_dirty = True def columnIsColor(self, column_name) -> bool: return (column_name == RAT_COLOR_HEADER_NAME ) or self.model.rat.fields[column_name].is_color def selectedColumnCanBeRemoved(self) -> bool: selected_column = self.mRATView.selectionModel().currentIndex().column( ) column_can_be_removed = False try: column_name = self.model.headers[selected_column] usage = self.model.rat.fields[column_name].usage if self.columnIsColor(column_name) or usage in {gdal.GFU_Generic}: column_can_be_removed = True elif usage == gdal.GFU_Name: # Check if it's not the only one names_count = len([ field for field in self.model.rat.fields.values() if field.usage == gdal.GFU_Name ]) if names_count > 1: column_can_be_removed = True except Exception as ex: rat_log('Could not get selected column type: %s' % ex) return column_can_be_removed def removeColumn(self): if not self.selectedColumnCanBeRemoved(): rat_log('Selected column cannot be removed!') else: selected_column = self.mRATView.selectionModel().currentIndex( ).column() column_name = self.model.headers[selected_column] # Handle color columns if self.columnIsColor(column_name): if QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove All Colors"), QCoreApplication.translate( 'RAT', "<p>All color information (Red, Green, Blue and Alpha) will be removed from the RAT.</p><p>Do you want continue?</p>" )) == QMessageBox.Yes: # Remove all colors if not self.model.remove_color(): QMessageBox.warning( None, QCoreApplication.translate( 'RAT', "Error Removing Colors"), QCoreApplication.translate( 'RAT', "An error occourred while removing colors from the RAT!" )) else: self.is_dirty = True # Normal columns elif QMessageBox.question( None, QCoreApplication.translate('RAT', "Remove Column"), QCoreApplication.translate( 'RAT', "Column <b>%s</b> will be removed from the RAT. Do you want continue?" % column_name)) == QMessageBox.Yes: result, error_message = self.model.remove_column( selected_column) if not result: QMessageBox.warning( None, QCoreApplication.translate('RAT', "Error Removing Column"), QCoreApplication.translate( 'RAT', "An error occourred while removing colum <b>%s</b> from the RAT: %s!" % (column_name, error_message))) else: self.is_dirty = True def updateButtons(self): enable_editing_buttons = self.mRATView.selectionModel().currentIndex( ).isValid() and self.editable self.mAddColumnToolButton.setEnabled(enable_editing_buttons and self.canAddAnyColumn()) self.mRemoveColumnToolButton.setEnabled(enable_editing_buttons) self.mAddRowToolButton.setEnabled(enable_editing_buttons) self.mRemoveRowToolButton.setEnabled(enable_editing_buttons) self.mSaveChangesToolButton.setEnabled(self.is_dirty) def setEditable(self, editable): if not editable and self.is_dirty: if QMessageBox.question( None, QCoreApplication.translate('RAT', "Save RAT changes"), QCoreApplication.translate( 'RAT', "RAT has been modified, Do you want to save the changes?" )) == QMessageBox.Yes: self.saveChanges() else: self.loadRat(self.mRasterBandsComboBox.currentIndex()) self.editable = editable self.model.setEditable(editable) self.updateButtons() def saveChanges(self): """Store changes back into the RAT""" rat = self.model.rat band = self.mRasterBandsComboBox.currentIndex() + 1 if rat.save(band): self.is_dirty = False def accept(self): QgsSettings().setValue("RasterAttributeTable/geometry", self.saveGeometry(), QgsSettings.Plugins) super().accept() def reject(self): if not self.is_dirty or QMessageBox.question( None, QCoreApplication.translate('RAT', "Save RAT changes"), QCoreApplication.translate( 'RAT', "<p>RAT has been modified, if you do not save the changes now or export the RAT they will be lost.</p><p>Exit without saving?<p>" )) == QMessageBox.Yes: self.accept() def classify(self): """Create classification on the selected criteria""" if QMessageBox.question( None, QCoreApplication.translate('RAT', "Overwrite classification"), QCoreApplication.translate( 'RAT', "The existing classification will be overwritten, do you want to continue?" )) == QMessageBox.Yes: band = self.mRasterBandsComboBox.currentIndex() + 1 criteria = self.mClassifyComboBox.currentText() # TODO: ramp & feedback unique_class_row_indexes = rat_classify(self.raster_layer, band, self.rat, criteria) unique_class_row_indexes.insert(0, 0) if self.iface is not None: deduplicate_legend_entries(self.iface, self.raster_layer, criteria, unique_class_row_indexes, expand=True) # Adopt the layer self.raster_layer.setCustomProperty( RAT_CUSTOM_PROPERTY_CLASSIFICATION_CRITERIA, criteria) def updateClassify(self): current_index = self.mClassifyComboBox.currentIndex() self.mClassifyComboBox.clear() self.mClassifyComboBox.addItems([ field.name for field in self.rat.fields.values() if field.usage in {gdal.GFU_Name, gdal.GFU_Generic} ]) if current_index > 0: self.mClassifyComboBox.setCurrentIndex(current_index) else: headers = self.rat.keys criteria = self.raster_layer.customProperty( RAT_CUSTOM_PROPERTY_CLASSIFICATION_CRITERIA) if criteria in headers: self.mClassifyComboBox.setCurrentIndex( self.mClassifyComboBox.findText(criteria)) def dirty(self, *args): self.is_dirty = True rat_log('Model is dirty') self.updateButtons() def loadRat(self, band_0_based) -> bool: """Load RAT for raster band 0-based""" if type(band_0_based) != int: rat_log( QCoreApplication.translate( 'RAT', 'Invalid band number for the selected raster.'), Qgis.Critical) return False self.mClassifyComboBox.clear() self.rat = get_rat(self.raster_layer, band_0_based + 1) if self.rat.keys: self.model = RATModel(self.rat) if os.environ.get('CI'): self.tester = QAbstractItemModelTester(self.model) self.model.dataChanged.connect(self.dirty) self.model.rowsInserted.connect(self.dirty) self.model.rowsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.dirty) self.model.columnsRemoved.connect(self.dirty) self.model.columnsInserted.connect(self.updateClassify) self.model.columnsRemoved.connect(self.updateClassify) self.proxyModel = QSortFilterProxyModel(self) self.proxyModel.setSourceModel(self.model) self.mRATView.setModel(self.proxyModel) self.mRATView.selectionModel().selectionChanged.connect( self.updateButtons) # Color picker if self.rat.has_color: if gdal.GFU_Alpha in self.rat.field_usages: colorDelegate = ColorAlphaDelegate(self.mRATView) else: colorDelegate = ColorDelegate(self.mRATView) self.mRATView.setItemDelegateForColumn(0, colorDelegate) self.updateClassify() self.mRATView.sortByColumn( self.model.headers.index(self.rat.value_columns[0]), Qt.AscendingOrder) return True else: rat_log( QCoreApplication.translate( 'RAT', 'There is no Raster Attribute Table for the selected raster.' ), Qgis.Critical) return False
class PdokServicesPlugin(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # docked or dialog, defaults to dialog # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if isinstance(QSettings().value("/pdokservicesplugin/docked"), QVariant): # self.docked = QSettings().value("/pdokservicesplugin/docked", QVariant(False)) # else: # self.docked = QSettings().value("/pdokservicesplugin/docked", False) # # # Create the dialog and keep reference # if "True" == self.docked or "true" == self.docked or True is self.docked: # self.dlg = PdokServicesPluginDockWidget() # self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dlg) # else: # self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) self.dlg = PdokServicesPluginDialog(parent=self.iface.mainWindow()) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path() + "/python/plugins/pdokservicesplugin" # initialize locale localePath = "" if isinstance(QSettings().value("locale/userLocale"), QVariant): locale = QSettings().value("locale/userLocale").value()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/pdokservicesplugin_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) self.currentLayer = None self.SETTINGS_SECTION = '/pdokservicesplugin/' self.pointer = None self.pdokgeocoder = PDOKGeoLocator(self.iface) self.geocoderSourceModel = None def getSettingsValue(self, key, default=''): if QSettings().contains(self.SETTINGS_SECTION + key): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 return str(QSettings().value(key).toString()) else: return str(QSettings().value(key)) else: return default def setSettingsValue(self, key, value): key = self.SETTINGS_SECTION + key if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue(key, QVariant(value)) else: QSettings().setValue(key, value) def initGui(self): # Create action that will start plugin configuration self.run_action = QAction(QIcon(":/plugins/pdokservicesplugin/icon.png"), \ u"Pdok Services Plugin", self.iface.mainWindow()) self.servicesLoaded = False # connect the action to the run method # 2018 may: RD: deprecating Docked window, as the content is getting to big anyway # if "True" == self.docked or "true" == self.docked or True == self.docked: # self.run_action.triggered.connect(self.showAndRaise) # self.dlg.radioDocked.setChecked(True) # # docked the dialog is immidiately visible, so should run NOW # else: # self.run_action.triggered.connect(self.run) # self.dlg.radioDocked.setChecked(False) # self.setupfq() self.run_action.triggered.connect(self.run) #self.dlg.radioDocked.setChecked(False) self.setupfq() # Add toolbar button and menu item #self.iface.addToolBarIcon(self.action) self.toolbar = self.iface.addToolBar("PDOK services plugin") self.toolbar.setObjectName("PDOK services plugin") self.toolbar.addAction(self.run_action) self.toolbarSearch = QLineEdit() self.toolbarSearch.setMaximumWidth(200) self.toolbarSearch.setAlignment(Qt.AlignLeft) self.toolbarSearch.setPlaceholderText("PDOK Locatieserver zoek") self.toolbar.addWidget(self.toolbarSearch) self.toolbarSearch.returnPressed.connect(self.searchAddressFromToolbar) # address/point cleanup self.clean_action = QAction(QIcon(":/plugins/pdokservicesplugin/eraser.png"), \ u"Cleanup", self.eraseAddress()) self.toolbar.addAction(self.clean_action) self.clean_action.triggered.connect(self.eraseAddress) self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.run_action) # about self.aboutAction = QAction(QIcon(":/plugins/pdokservicesplugin/help.png"), \ "About", self.iface.mainWindow()) self.aboutAction.setWhatsThis("Pdok Services Plugin About") self.iface.addPluginToMenu(u"&Pdok Services Plugin", self.aboutAction) self.aboutAction.triggered.connect(self.about) self.dlg.ui.btnLoadLayer.clicked.connect(self.loadService) self.dlg.geocoderSearch.returnPressed.connect(self.searchAddress) self.dlg.geocoderSearch.textEdited.connect(self.searchAddress) self.dlg.geocoderSearch.setPlaceholderText("PDOK Locatieserver zoek, bv postcode of postcode huisnummer") self.dlg.geocoderResultSearch.textChanged.connect(self.filterGeocoderResult) self.dlg.geocoderResultSearch.setPlaceholderText("een of meer zoekwoorden uit resultaat") #self.dlg.radioDocked.toggled.connect(self.set_docked) self.dlg.btnCheckPdokJson.clicked.connect(self.checkPdokJson) #self.iface.mapCanvas().renderStarting.connect(self.extentsChanged) ui = self.dlg.ui cbxs = [ui.cbx_gem, ui.cbx_wpl, ui.cbx_weg, ui.cbx_pcd, ui.cbx_adr, ui.cbx_pcl, ui.cbx_hmp] # connect all fq checkboxes with suggest, so upon a change in fq filter we re-search for cbx in cbxs: cbx.stateChanged.connect(self.searchAddress) self.run(True) # for now hiding the pointer as soon as the extent changes #def extentsChanged(self): # self.removePointer() def checkPdokJson(self): myversion = self.getSettingsValue('pdokversion', '1') msgtxt = '' msglvl = 0 # QgsMessageBar.INFO try: response = urllib.request.urlopen('http://www.qgis.nl/pdok.version') str_response = response.read().decode('utf-8') pdokversion = json.loads(str_response) if pdokversion > int(myversion): response = urllib.request.urlopen('http://www.qgis.nl/pdok.json') str_response = response.read().decode('utf-8') pdokjson = json.loads(str_response) with open(self.plugin_dir +'/pdok.json', 'w') as outfile: json.dump(pdokjson, outfile) msgtxt = "De laatste versie is opgehaald en zal worden gebruikt " + \ str(pdokversion) + ' (was ' + myversion +')' self.servicesLoaded = False # reset reading of json self.run() self.setSettingsValue('pdokversion', pdokversion) else: msgtxt = "Geen nieuwere versie beschikbaar dan " + str(pdokversion) except Exception as e: #print e msgtxt = "Fout bij ophalen van service info. Netwerk probleem?" msglvl = 2 # QgsMessageBar.CRITICAL # msg if hasattr(self.iface, 'messageBar'): self.iface.messageBar().pushMessage("PDOK services update", msgtxt, level=msglvl, duration=10) else: # 1.8 QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin", msgtxt) # def set_docked(self, foo): # self.setSettingsValue('docked', self.dlg.radioDocked.isChecked()) # #if Qgis.QGIS_VERSION_INT < 10900: # # # qgis <= 1.8 # # QSettings().setValue("/pdokservicesplugin/docked", QVariant(self.dlg.radioDocked.isChecked())) # #else: # # QSettings().setValue("/pdokservicesplugin/docked", self.dlg.radioDocked.isChecked()) def showAndRaise(self): self.dlg.show() self.dlg.raise_() # also remove the pointer self.removePointer() def about(self): infoString = "Written by Richard Duivenvoorde\nEmail - [email protected]\n" infoString += "Company - Zuidt - http://www.zuidt.nl\n" infoString += "Source: https://github.com/rduivenvoorde/pdokservicesplugin" QMessageBox.information(self.iface.mainWindow(), "Pdok Services Plugin About", infoString) def unload(self): self.removePointer() # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Pdok Services Plugin",self.run_action) del self.toolbarSearch def showService(self, selectedIndexes): if len(selectedIndexes)==0: self.currentLayer = None self.dlg.ui.layerInfo.setHtml('') self.dlg.ui.comboSelectProj.clear() return # needed to scroll To the selected row incase of using the keyboard / arrows self.dlg.servicesView.scrollTo(self.dlg.servicesView.selectedIndexes()[0]) # itemType holds the data (== column 1) self.currentLayer = self.dlg.servicesView.selectedIndexes()[1].data(Qt.UserRole) if isinstance(self.currentLayer, QVariant): self.currentLayer = self.currentLayer.toMap() # QGIS 1.8: QVariants currentLayer = {} for key in list(self.currentLayer.keys()): val = self.currentLayer[key] currentLayer[str(key)]=str(val.toString()) self.currentLayer = currentLayer url = self.currentLayer['url'] title = self.currentLayer['title'] style = '' if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' servicetitle = self.currentLayer['servicetitle'] layername = self.currentLayer['layers'] abstract = self.currentLayer['abstract'] stype = self.currentLayer['type'].upper() minscale ='' if 'minscale' in self.currentLayer and self.currentLayer['minscale'] != None and self.currentLayer['minscale'] != '': minscale = "min. schaal 1:"+self.currentLayer['minscale'] maxscale = '' if 'maxscale' in self.currentLayer and self.currentLayer['maxscale'] != None and self.currentLayer['maxscale'] != '': maxscale = "max. schaal 1:"+self.currentLayer['maxscale'] self.dlg.ui.layerInfo.setText('') self.dlg.ui.btnLoadLayer.setEnabled(True) self.dlg.ui.layerInfo.setHtml('<h4>%s</h4><h3>%s</h3><lu><li>%s</li><li> </li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li><li>%s</li></lu>' % (servicetitle, title, abstract, stype, url, layername, style, minscale, maxscale)) self.dlg.ui.comboSelectProj.clear() if stype=="WMS": try: crs = self.currentLayer['crs'] except KeyError: crs = 'EPSG:28992' crs = crs.split(',') self.dlg.ui.comboSelectProj.addItems(crs) for i in range(len(crs)): if crs[i] == 'EPSG:28992': self.dlg.ui.comboSelectProj.setCurrentIndex(i) if stype=="WMTS": tilematrixsets = self.currentLayer['tilematrixsets'].split(',') self.dlg.ui.comboSelectProj.addItems(tilematrixsets) for i in range(len(tilematrixsets)): if tilematrixsets[i].startswith('EPSG:28992'): self.dlg.ui.comboSelectProj.setCurrentIndex(i) def loadService(self): if self.currentLayer == None: return servicetype = self.currentLayer['type'] url = self.currentLayer['url'] # some services have an url with query parameters in it, we have to urlencode those: location,query = urllib.parse.splitquery(url) url = location if query != None and query != '': url +=('?'+urllib.parse.quote_plus(query)) title = self.currentLayer['title'] if 'style' in self.currentLayer: style = self.currentLayer['style'] title = title + ' [' + style + ']' else: style = '' # == default for this service layers = self.currentLayer['layers'] # mmm, tricky: we take the first one while we can actually want png/gif or jpeg if servicetype == "wms": imgformat = self.currentLayer['imgformats'].split(',')[0] if self.dlg.ui.comboSelectProj.currentIndex() == -1: crs = 'EPSG:28992' else: crs = self.dlg.ui.comboSelectProj.currentText() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 uri = url self.iface.addRasterLayer( uri, # service uri title, # name for layer (as seen in QGIS) "wms", # dataprovider key [layers], # array of layername(s) for provider (id's) [""], # array of stylename(s) NOTE: ignoring styles here!!! imgformat, # image format searchstring crs) # crs code searchstring else: # qgis > 1.8 uri = "crs="+crs+"&layers="+layers+"&styles="+style+"&format="+imgformat+"&url="+url; self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wmts": if Qgis.QGIS_VERSION_INT < 10900: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen in deze versie van QGIS.\nMisschien kunt u QGIS 2.0 installeren (die kan het WEL)?\nOf is de laag niet ook beschikbaar als wms of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return if self.dlg.ui.comboSelectProj.currentIndex() == -1: tilematrixset = 'EPSG:28992' else: tilematrixset = self.dlg.ui.comboSelectProj.currentText() imgformat = self.currentLayer['imgformats'].split(',')[0] # special case for luchtfoto #if layers=="luchtfoto": # # tileMatrixSet=nltilingschema&crs=EPSG:28992&layers=luchtfoto&styles=&format=image/jpeg&url=http://geodata1.nationaalgeoregister.nl/luchtfoto/wmts/1.0.0/WMTSCapabilities.xml # # {u'layers': u'luchtfoto', u'imgformats': u'image/jpeg', u'title': u'PDOK-achtergrond luchtfoto', u'url': u'http://geodata1.nationaalgeoregister.nl/luchtfoto/wms', u'abstract': u'', u'tilematrixsets': u'nltilingschema', u'type': u'wmts'} # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url #else: # uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=&format="+imgformat+"&url="+url; #uri = "tileMatrixSet="+tilematrixsets+"&crs=EPSG:28992&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; if tilematrixset.startswith('EPSG:'): crs=tilematrixset i = crs.find(':', 5) if i > -1: crs=crs[:i] elif tilematrixset.startswith('OGC:1.0'): crs='EPSG:3857' uri = "tileMatrixSet="+tilematrixset+"&crs="+crs+"&layers="+layers+"&styles=default&format="+imgformat+"&url="+url; #print "############ PDOK URI #################" #print uri self.iface.addRasterLayer(uri, title, "wms") elif servicetype == "wfs": location, query = urllib.parse.splitquery(url) #uri = location+"?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME="+layers+"&SRSNAME=EPSG:28992" #uri = location + "?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=" + layers + "&SRSNAME=EPSG:28992" # adding a bbox paramater forces QGIS to NOT cache features but retrieve new features all the time # QGIS will update the BBOX to the right value #uri += "&BBOX=-10000,310000,290000,650000" uri = " pagingEnabled='true' restrictToRequestBBOX='1' srsname='EPSG:28992' typename='"+layers+"' url='"+url+"' version='2.0.0' " self.iface.addVectorLayer(uri, title, "WFS") elif servicetype == "wcs": # cache=AlwaysCache&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs uri = '' # cache=AlwaysCache # cache=PreferNetwork # cache=AlwaysNetwork # cache=AlwaysNetwork&crs=EPSG:28992&format=GeoTIFF&identifier=ahn25m:ahn25m&url=http://geodata.nationaalgeoregister.nl/ahn25m/wcs #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.1&identifier="+layers+"&url="+url # working for ahn1 ahn2 and ahn3: GEOTIFF_FLOAT32 format = 'GEOTIFF_FLOAT32' # working for ahn25m is only image/tiff if layers=='ahn25m': format = 'image/tiff' # we handcrated some wcs layers with 2 different image formats: tiff (RGB) and tiff (float32): if 'imgformats' in self.currentLayer: format = self.currentLayer['imgformats'].split(',')[0] uri = "cache=AlwaysNetwork&crs=EPSG:28992&format="+format+"&version=1.1.2&identifier=" + layers + "&url=" + url #uri = "cache=AlwaysNetwork&crs=EPSG:28992&format=image/tiff&version=1.1.2&identifier=" + layers + "&url=" + url self.iface.addRasterLayer(uri, title, "wcs") else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ("Sorry, dit type layer: '"+servicetype.upper()+"' \nkan niet worden geladen door de plugin of door QGIS.\nIs het niet beschikbaar als wms, wmts of wfs?"), QMessageBox.Ok, QMessageBox.Ok) return def filterGeocoderResult(self, string): #print "filtering geocoder results: %s" % string self.dlg.geocoderResultView.selectRow(0) self.geocoderProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.geocoderProxyModel.setFilterFixedString(string) def searchAddressFromToolbar(self): self.removePointer() self.geocoderSourceModel.clear() self.geocode() def searchAddress(self): self.removePointer() #print "search geocoder for: %s" % self.dlg.geocoderSearch.text() self.geocoderSourceModel.clear() #self.geocode(self.dlg.geocoderSearch.text()) self.suggest() def eraseAddress(self): """ clean the input and remove the pointer """ self.removePointer() if self.geocoderSourceModel is not None: self.geocoderSourceModel.clear() if self.dlg.geocoderSearch is not None: self.dlg.geocoderSearch.clear() if self.toolbarSearch is not None: self.toolbarSearch.clear() def filterLayers(self, string): # remove selection if one row is selected self.dlg.servicesView.selectRow(0) #self.currentLayer = None self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterFixedString(string) #def addSourceRow(self, service, layer): def addSourceRow(self, serviceLayer): # you can attache different "data's" to to an QStandarditem # default one is the visible one: itemType = QStandardItem("%s" % (serviceLayer["type"].upper()) ) # userrole is a free form one: # only attach the data to the first item # service layer = a dict/object with all props of the layer itemType.setData( serviceLayer, Qt.UserRole ) itemType.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) # only wms services have styles (sometimes) layername = serviceLayer["title"] if 'style' in serviceLayer: itemLayername = QStandardItem("%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) ) layername = "%s [%s]" % (serviceLayer["title"], serviceLayer["style"]) else: itemLayername = QStandardItem("%s" % (serviceLayer["title"])) itemLayername.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["servicetitle"] )) # itemFilter is the item used to search filter in. That is why layername is a combi of layername + filter here itemFilter = QStandardItem("%s %s %s %s" % (serviceLayer["type"], layername, serviceLayer["servicetitle"], serviceLayer["abstract"]) ) itemServicetitle = QStandardItem("%s" % (serviceLayer["servicetitle"])) itemServicetitle.setToolTip("%s - %s" % (serviceLayer["type"].upper() ,serviceLayer["title"] )) self.sourceModel.appendRow( [ itemLayername, itemType, itemServicetitle, itemFilter ] ) # run method that performs all the real work def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer() def setupfq(self): """ Setup the fq checkboxes in the gui, by looking into the settings for the 'pdokservicesplugin/checkedfqs' key, which contains a list of type strings like ['weg','adres'] """ checked_fqs = self.getSettingsValue('checkedfqs', []) #self.info('setup fq: {}'.format(checked_fqs)) if len(checked_fqs) > 0: # else there is not saved state... take gui defaults self.dlg.ui.cbx_gem.setChecked('gemeente' in checked_fqs) self.dlg.ui.cbx_wpl.setChecked('woonplaats' in checked_fqs) self.dlg.ui.cbx_weg.setChecked('weg' in checked_fqs) self.dlg.ui.cbx_pcd.setChecked('postcode' in checked_fqs) self.dlg.ui.cbx_adr.setChecked('adres' in checked_fqs) self.dlg.ui.cbx_pcl.setChecked('perceel' in checked_fqs) self.dlg.ui.cbx_hmp.setChecked('hectometerpaal' in checked_fqs) def createfq(self): """ This creates a fq-string (Filter Query, see https://github.com/PDOK/locatieserver/wiki/Zoekvoorbeelden-Locatieserver) Based on the checkboxes in the dialog. Defaults to '' Example: 'fq=+type:adres+type:gemeente' (only gemeente AND addresses) :return: """ fqlist = [] if self.dlg.ui.cbx_gem.isChecked(): fqlist.append('gemeente') if self.dlg.ui.cbx_wpl.isChecked(): fqlist.append('woonplaats') if self.dlg.ui.cbx_weg.isChecked(): fqlist.append('weg') if self.dlg.ui.cbx_pcd.isChecked(): fqlist.append('postcode') if self.dlg.ui.cbx_adr.isChecked(): fqlist.append('adres') if self.dlg.ui.cbx_pcl.isChecked(): fqlist.append('perceel') if self.dlg.ui.cbx_hmp.isChecked(): fqlist.append('hectometerpaal') self.setSettingsValue('checkedfqs', fqlist) #self.info(self.getSettingsValue('checkedfqs', ['leeg?'])) fq = '' if len(fqlist) > 0: fq = '&fq=+type:' + '+type:'.join(fqlist) return fq def suggest(self): self.dlg.ui.lookupinfo.setHtml('') search_text = self.dlg.geocoderSearch.text() if len(search_text) <= 1: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "meer input aub: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) return # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "zoeken: {}".format(search_text) # ), QMessageBox.Ok, QMessageBox.Ok) results = self.pdokgeocoder.suggest(search_text, self.createfq()) if len(results) == 0: # ignore, as we are suggesting, maybe more characters will reveal something... return for result in results: #print address adrestekst = QStandardItem("%s" % (result["adrestekst"])) adrestekst.setData(result, Qt.UserRole) type = QStandardItem("%s" % (result["type"])) id = QStandardItem("%s" % (result["id"])) score = QStandardItem("%s" % (result["score"])) adrestekst.setData(result, Qt.UserRole) self.geocoderSourceModel.appendRow([adrestekst, type]) self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) self.dlg.geocoderResultView.resizeColumnsToContents() self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def geocode(self): self.dlg.geocoderSearch.setText(self.toolbarSearch.text()) self.suggest() if self.dlg.geocoderResultView.model().rowCount()>0: self.dlg.geocoderResultView.selectRow(0) self.zoomToAddress() else: QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ "Niets gevonden.\nProbeer een andere spelling, of alleen postcode/huisnummer?\n\nSelecteer meer (Locatieserver) 'typen' in de PdokServicesPlugin dialoog.\n\nOf gebruik de 'PDOK geocoder'-tab in de PdokServicesPlugin dialoog." ), QMessageBox.Ok, QMessageBox.Ok) # def geocode(self): # self.dlg.ui.lookupinfo.setHtml('') # search_text = self.toolbarSearch.text() # addresses = self.pdokgeocoder.search(search_text) # if len(addresses) == 0: # QMessageBox.warning(self.iface.mainWindow(), "PDOK plugin", ( \ # "Niets gevonden. Probeer een andere spelling of alleen postcode/huisnummer." # ), QMessageBox.Ok, QMessageBox.Ok) # return # for address in addresses: # #print address # adrestekst = QStandardItem("%s" % (address["adrestekst"])) # adrestekst.setData(address, Qt.UserRole) # straat = QStandardItem("%s" % (address["straat"])) # nummer = QStandardItem("%s" % (address["nummer"])) # postcode = QStandardItem("%s" % (address["postcode"])) # plaats = QStandardItem("%s" % (address["plaats"])) # gemeente = QStandardItem("%s" % (address["gemeente"])) # provincie = QStandardItem("%s" % (address["provincie"])) # self.geocoderSourceModel.appendRow([adrestekst, straat, nummer, postcode, plaats, gemeente, provincie]) # # self.dlg.geocoderResultView.selectRow(0) # self.zoomToAddress() # # self.geocoderSourceModel.setHeaderData(0, Qt.Horizontal, "Resultaat") # self.geocoderSourceModel.setHeaderData(1, Qt.Horizontal, "Straat") # self.geocoderSourceModel.setHeaderData(2, Qt.Horizontal, "Nr") # self.geocoderSourceModel.setHeaderData(3, Qt.Horizontal, "Postcode") # self.geocoderSourceModel.setHeaderData(4, Qt.Horizontal, "Plaats") # self.geocoderSourceModel.setHeaderData(5, Qt.Horizontal, "Gemeente") # self.geocoderSourceModel.setHeaderData(6, Qt.Horizontal, "Provincie") # # self.geocoderSourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(3).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(4).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(5).setTextAlignment(Qt.AlignLeft) # self.geocoderSourceModel.horizontalHeaderItem(6).setTextAlignment(Qt.AlignLeft) # # self.dlg.geocoderResultView.resizeColumnsToContents() # self.dlg.geocoderResultView.horizontalHeader().setStretchLastSection(True) def zoomToAddress(self): # get x,y from data of record self.removePointer() data = self.dlg.geocoderResultView.selectedIndexes()[0].data(Qt.UserRole) if 'centroide_rd' in data: # free OR lookup service geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] else: # no centroid yet, probably only object id, retrieve it via lookup service id = data['id'] data = self.pdokgeocoder.lookup(id) geom = QgsGeometry.fromWkt(data['centroide_rd']) adrestekst = data['adrestekst'] lookup_data= data['data'] lis = '' for key in lookup_data.keys(): lis = lis + '<li>{}: {}</li>'.format(key, lookup_data[key]) self.dlg.ui.lookupinfo.setHtml( '<h4>{}</h4><lu>{}</lu>'.format(adrestekst, lis)) # just always transform from 28992 to mapcanvas crs crs = self.iface.mapCanvas().mapSettings().destinationCrs() crs28992 = QgsCoordinateReferenceSystem() crs28992.createFromId(28992) crsTransform = QgsCoordinateTransform(crs28992, crs, QgsProject.instance()) z = 1587 if adrestekst.lower().startswith('adres'): z = 794 elif adrestekst.lower().startswith('perceel'): z = 794 elif adrestekst.lower().startswith('hectometer'): z = 1587 elif adrestekst.lower().startswith('straat'): z = 3175 elif adrestekst.lower().startswith('postcode'): z = 6350 elif adrestekst.lower().startswith('woonplaats'): z = 25398 elif adrestekst.lower().startswith('gemeente'): z = 50797 elif adrestekst.lower().startswith('provincie'): z = 812750 geom.transform(crsTransform) center = geom.asPoint() self.setPointer(center) # zoom to with center is actually setting a point rectangle and then zoom rect = QgsRectangle(center, center) self.iface.mapCanvas().setExtent(rect) self.iface.mapCanvas().zoomScale(z) self.iface.mapCanvas().refresh() def setPointer(self, point): self.removePointer() self.pointer = QgsVertexMarker(self.iface.mapCanvas()) self.pointer.setColor(QColor(255, 255, 0)) self.pointer.setIconSize(10) self.pointer.setPenWidth(5) self.pointer.setCenter(point) def removePointer(self): if self.pointer is not None: self.iface.mapCanvas().scene().removeItem(self.pointer) def info(self, msg=""): QgsMessageLog.logMessage('{}'.format(msg), 'PDOK-services Plugin', Qgis.Info)
def run(self, hiddenDialog=False): # enable possible remote pycharm debugging #import pydevd #pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # last viewed/selected tab if QSettings().contains("/pdokservicesplugin/currenttab"): if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 self.dlg.tabs.widget(QSettings().value("/pdokservicesplugin/currenttab").toInt()[0]) else: self.dlg.tabs.widget(int(QSettings().value("/pdokservicesplugin/currenttab"))) if self.servicesLoaded == False: pdokjson = os.path.join(os.path.dirname(__file__), ".", "pdok.json") f = open(pdokjson, 'r', encoding='utf-8') self.pdok = json.load(f) f.close() self.proxyModel = QSortFilterProxyModel() self.sourceModel = QStandardItemModel() self.proxyModel.setSourceModel(self.sourceModel) # filter == search on itemFilter column: self.proxyModel.setFilterKeyColumn(3) self.dlg.servicesView.setModel(self.proxyModel) self.dlg.servicesView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.geocoderProxyModel = QSortFilterProxyModel() self.geocoderSourceModel = QStandardItemModel() self.geocoderProxyModel.setSourceModel(self.geocoderSourceModel) self.geocoderProxyModel.setFilterKeyColumn(0) self.dlg.geocoderResultView.setModel(self.geocoderProxyModel) self.dlg.geocoderResultView.setEditTriggers(QAbstractItemView.NoEditTriggers) #{"services":[ # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"}, # {"naam":"WMS NHI","url":"http://geodata.nationaalgeoregister.nl/nhi/ows","layers":["dmlinks","dmnodes"],"type":"wms"} # ]} # for service in self.pdok["services"]: # service[layer] was an array if isinstance(service["layers"], str) or isinstance(service["layers"], str): self.addSourceRow(service) self.dlg.layerSearch.textChanged.connect(self.filterLayers) self.dlg.layerSearch.setPlaceholderText("woord uit laagnaam, type of service ") self.dlg.servicesView.selectionModel().selectionChanged.connect(self.showService) self.dlg.servicesView.doubleClicked.connect(self.loadService) # actually I want to load a service when doubleclicked on header # but as I cannot get this to work, let's disable clicking it then self.dlg.servicesView.verticalHeader().setSectionsClickable(False) self.dlg.servicesView.horizontalHeader().setSectionsClickable(False) #self.dlg.geocoderResultView.doubleClicked.connect(self.zoomToAddress) self.dlg.geocoderResultView.selectionModel().selectionChanged.connect(self.zoomToAddress) # hide itemFilter column: self.dlg.servicesView.hideColumn(3) self.servicesLoaded = True; self.sourceModel.setHeaderData(2, Qt.Horizontal, "Service") self.sourceModel.setHeaderData(1, Qt.Horizontal, "Type") self.sourceModel.setHeaderData(0, Qt.Horizontal, "Laagnaam [style]") self.sourceModel.horizontalHeaderItem(2).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(1).setTextAlignment(Qt.AlignLeft) self.sourceModel.horizontalHeaderItem(0).setTextAlignment(Qt.AlignLeft) #self.dlg.servicesView.verticalHeader().hide() #self.dlg.servicesView.resizeColumnsToContents() self.dlg.servicesView.setColumnWidth(0, 300) # set name to 300px (there are some huge layernames) self.dlg.servicesView.horizontalHeader().setStretchLastSection(True) # show the dialog ? if not hiddenDialog: self.dlg.show() # Run the dialog event loop #result = self.dlg.exec_() if Qgis.QGIS_VERSION_INT < 10900: # qgis <= 1.8 QSettings().setValue("/pdokservicesplugin/currenttab", QVariant(self.dlg.tabs.currentIndex())) else: QSettings().setValue("/pdokservicesplugin/currenttab", self.dlg.tabs.currentIndex()) self.removePointer()
def makeProxyModel(self, model): proxyModel = QSortFilterProxyModel() proxyModel.setDynamicSortFilter(True) proxyModel.setSortCaseSensitivity(Qt.CaseInsensitive) proxyModel.setSourceModel(model) return proxyModel
class STRViewEntityWidget(WIDGET2, BASE2, EntitySearchItem): """ A widget that represents options for searching through an entity. """ asyncStarted = pyqtSignal() asyncFinished = pyqtSignal() def __init__(self, config, formatter=None, parent=None): QWidget.__init__(self, parent) EntitySearchItem.__init__(self, formatter) self.setupUi(self) self.tbSTRViewEntity.setTabIcon(0, GuiUtils.get_icon('filter.png')) self.tbSTRViewEntity.setTabIcon(1, GuiUtils.get_icon('period_blue.png')) self.config = config self.setConfigOptions() self.curr_profile = current_profile() self.social_tenure = self.curr_profile.social_tenure self.str_model = entity_model(self.social_tenure) # Model for storing display and actual mapping values self._completer_model = None self._proxy_completer_model = None # Hook up signals self.cboFilterCol.currentIndexChanged.connect( self._on_column_index_changed) self.init_validity_dates() self.validity_from_date.dateChanged.connect(self.set_minimum_to_date) self.validity.setDisabled(True) self.init_validity_checkbox() def init_validity_checkbox(self): self.check_box_list = [] self.validity_checkbox = QCheckBox() self.check_box_list.append(self.validity_checkbox) self.tbSTRViewEntity.tabBar().setTabButton( self.tbSTRViewEntity.tabBar().count() - 1, QTabBar.LeftSide, self.validity_checkbox) self.validity_checkbox.stateChanged.connect( self.toggle_validity_period) def toggle_validity_period(self, state): if state == Qt.Checked: self.validity.setDisabled(False) else: self.validity.setDisabled(True) def set_minimum_to_date(self): """ Set the minimum to date based on the change in value of from date. :return: :rtype: """ self.validity_to_date.setMinimumDate(self.validity_from_date.date()) def init_validity_dates(self): """ Initialize the dates by setting the current date. :return: :rtype: """ self.validity_from_date.setDate(date.today()) self.validity_to_date.setDate(date.today()) def setConfigOptions(self): """ Apply configuration options. """ # Set filter columns and remove id column for col_name, display_name in self.config.filterColumns.items(): if col_name != "id": self.cboFilterCol.addItem(display_name, col_name) def loadAsync(self): """ Asynchronously loads an entity's attribute values. """ self.asyncStarted.emit() # Create model worker workerThread = QThread(self) modelWorker = ModelWorker() modelWorker.moveToThread(workerThread) # Connect signals modelWorker.error.connect(self.errorHandler) workerThread.started.connect(lambda: modelWorker.fetch( self.config.STRModel, self.currentFieldName())) modelWorker.retrieved.connect(self._asyncFinished) modelWorker.retrieved.connect(workerThread.quit) workerThread.finished.connect(modelWorker.deleteLater) workerThread.finished.connect(workerThread.deleteLater) # Start thread workerThread.start() def validate(self): """ Validate entity search widget """ is_valid = True message = "" if self.txtFilterPattern.text() == "": message = QApplication.translate("ViewSTR", "Search word cannot be empty.") is_valid = False return is_valid, message def executeSearch(self): """ Base class override. Search for matching items for the specified entity and column. """ model_root_node = None prog_dialog = QProgressDialog(self) prog_dialog.setFixedWidth(380) prog_dialog.setWindowTitle( QApplication.translate("STRViewEntityWidget", "Searching for STR...")) prog_dialog.show() prog_dialog.setRange(0, 10) search_term = self._searchTerm() prog_dialog.setValue(2) # Try to get the corresponding search term value from the completer model if not self._completer_model is None: reg_exp = QRegExp("^%s$" % (search_term), Qt.CaseInsensitive, QRegExp.RegExp2) self._proxy_completer_model.setFilterRegExp(reg_exp) if self._proxy_completer_model.rowCount() > 0: # Get corresponding actual value from the first matching item value_model_idx = self._proxy_completer_model.index(0, 1) source_model_idx = self._proxy_completer_model.mapToSource( value_model_idx) prog_dialog.setValue(4) search_term = self._completer_model.data( source_model_idx, Qt.DisplayRole) modelInstance = self.config.STRModel() modelQueryObj = modelInstance.queryObject() queryObjProperty = getattr(self.config.STRModel, self.currentFieldName()) entity_name = modelQueryObj._primary_entity._label_name entity = self.curr_profile.entity_by_name(entity_name) prog_dialog.setValue(6) # Get property type so that the filter can # be applied according to the appropriate type propType = queryObjProperty.property.columns[0].type results = [] try: if not isinstance(propType, String): col_name = self.currentFieldName() col = entity.columns[self.currentFieldName()] if col.TYPE_INFO == 'LOOKUP': lookup_entity = lookup_parent_entity( self.curr_profile, col_name) lkp_model = entity_model(lookup_entity) lkp_obj = lkp_model() value_obj = getattr(lkp_model, 'value') result = lkp_obj.queryObject().filter( func.lower(value_obj) == func.lower( search_term)).first() if result is None: result = lkp_obj.queryObject().filter( func.lower(value_obj).like(search_term + '%')).first() if not result is None: results = modelQueryObj.filter( queryObjProperty == result.id).all() else: results = [] else: results = modelQueryObj.filter( func.lower(queryObjProperty) == func.lower( search_term)).all() if self.validity.isEnabled(): valid_str_ids = self.str_validity_period_filter(results) else: valid_str_ids = None prog_dialog.setValue(7) except exc.StatementError: return model_root_node, [], search_term # if self.formatter is not None: # self.formatter.setData(results) # model_root_node = self.formatter.root(valid_str_ids) prog_dialog.setValue(10) prog_dialog.hide() return results, search_term def str_validity_period_filter(self, results): """ Filter the entity results using validity period in STR table. :param results: Entity result :type results: SQLAlchemy result proxy :return: Valid list of STR ids :rtype: List """ self.str_model_obj = self.str_model() valid_str_ids = [] for result in results: from_date = self.validity_from_date.date().toPyDate() to_date = self.validity_to_date.date().toPyDate() entity_id = '{}_id'.format(result.__table__.name[3:]) str_column_obj = getattr(self.str_model, entity_id) str_result = self.str_model_obj.queryObject().filter( self.str_model.validity_start >= from_date).filter( self.str_model.validity_end <= to_date).filter( str_column_obj == result.id).all() for res in str_result: valid_str_ids.append(res.id) return valid_str_ids def reset(self): """ Clear search input parameters. """ self.txtFilterPattern.clear() if self.cboFilterCol.count() > 0: self.cboFilterCol.setCurrentIndex(0) def currentFieldName(self): """ Returns the name of the database field from the current item in the combo box. """ curr_index = self.cboFilterCol.currentIndex() field_name = self.cboFilterCol.itemData(curr_index) if field_name is None: return else: return field_name def _searchTerm(self): """ Returns the search term specified by the user. """ return self.txtFilterPattern.text() def _asyncFinished(self, model_values): """ Slot raised when worker has finished retrieving items. """ # Create QCompleter and add values to it. self._update_completer(model_values) self.asyncFinished.emit() def _update_completer(self, values): # Get the items in a tuple and put them in a list # Store display and actual values in a # model for easier mapping and # retrieval when carrying out searches model_attr_mapping = [] # Check if there are formaters specified # for the current field name for mv in values: f_model_values = [] m_val = mv[0] if m_val is not None: col_label = self.currentFieldName() if col_label in self.config.LookupFormatters: formatter = self.config.LookupFormatters[col_label] if formatter.column.TYPE_INFO == 'LOOKUP': m_val = formatter.code_value(m_val)[0] else: m_val = formatter.format_column_value(m_val) f_model_values.extend([m_val, m_val]) model_attr_mapping.append(f_model_values) self._completer_model = BaseSTDMTableModel(model_attr_mapping, ["", ""], self) # We will use the QSortFilterProxyModel for filtering purposes self._proxy_completer_model = QSortFilterProxyModel() self._proxy_completer_model.setDynamicSortFilter(True) self._proxy_completer_model.setSourceModel(self._completer_model) self._proxy_completer_model.setSortCaseSensitivity(Qt.CaseInsensitive) self._proxy_completer_model.setFilterKeyColumn(0) # Configure completer mod_completer = QCompleter(self._completer_model, self) mod_completer.setCaseSensitivity(Qt.CaseInsensitive) mod_completer.setCompletionMode(QCompleter.PopupCompletion) mod_completer.setCompletionColumn(0) mod_completer.setCompletionRole(Qt.DisplayRole) self.txtFilterPattern.setCompleter(mod_completer) def _on_column_index_changed(self, int): """ Slot raised when the user selects a different filter column. """ self.txtFilterPattern.clear() self.loadAsync()
class ExtendedCombobox(QComboBox): """ Overwrite combobox to provide text filtering of combobox list. """ def __init__(self, parent): """ Initialise ExtendedCombobox :param parent: Parent of combobox :type parent: PyQt5.QtWidgets.QWidget """ super().__init__(parent) self.setFocusPolicy(Qt.StrongFocus) self.setEditable(True) self.completer = QCompleter(self) # always show all completions self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.p_filter_model = QSortFilterProxyModel(self) self.p_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.completer.setPopup(self.view()) self.setCompleter(self.completer) self.lineEdit().textEdited.connect(self.p_filter_model.setFilterFixedString) self.completer.activated.connect(self.setTextIfCompleterIsClicked) def setModel(self, model): # pylint:disable=invalid-name """ Set the model to use the Filter model :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ super().setModel(model) self.p_filter_model.setSourceModel(model) self.completer.setModel(self.p_filter_model) def setModelColumn(self, column): # pylint:disable=invalid-name """ :param model: The model to be used by the combobox :type model: PyQt5.QtGui.QStandardItemModel """ self.completer.setCompletionColumn(column) self.p_filter_model.setFilterKeyColumn(column) super().setModelColumn(column) def view(self): """ A QListView of items stored in the model :return: items stored in the model :rtype: PyQt5.QtWidgets.QListView """ return self.completer.popup() def index(self): """ Index of the current item in the combobox. :return: index of the current item :rtype: int """ return self.currentIndex() def setTextIfCompleterIsClicked(self, text): # pylint:disable=invalid-name """ :param text: The current text of the qlineedit :type text: str If the combobx lineedit is clicked, set the lineedits current item as the combobox's current item """ if text: index = self.findText(text) self.setCurrentIndex(index)
def __init__( self, table_data, headers, parent=None, *args ): """ Creates two QTableViews one of which is a frozen table while the other one can scroll behind it. :param table_data: The data that goes into the tables :type table_data: List :param headers: The header data of the tables. :type headers: List :param parent: The parent of the QTableView :type parent: QWidget :param args: :type args: """ QTableView.__init__(self, parent) # set the table model self.table_model = BaseSTDMTableModel( table_data, headers, parent ) # set the proxy model proxy_model = QSortFilterProxyModel(self) proxy_model.setSourceModel(self.table_model) # Assign a data model for TableView self.setModel(self.table_model) # frozen_table_view - first column self.frozen_table_view = QTableView(self) # Set the model for the widget, fixed column self.frozen_table_view.setModel(self.table_model) # Hide row headers self.frozen_table_view.verticalHeader().hide() # Widget does not accept focus self.frozen_table_view.setFocusPolicy( Qt.StrongFocus | Qt.TabFocus | Qt.ClickFocus ) # The user can not resize columns self.frozen_table_view.horizontalHeader(). \ setSectionResizeMode(QHeaderView.Fixed) self.frozen_table_view.setObjectName('frozen_table') self.setSelectionMode(QAbstractItemView.NoSelection) # Remove the scroll bar self.frozen_table_view.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff ) self.frozen_table_view.setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff ) # Puts more widgets to the foreground self.viewport().stackUnder(self.frozen_table_view) # # Log in to edit mode - even with one click # Set the properties of the column headings hh = self.horizontalHeader() # Text alignment centered hh.setDefaultAlignment(Qt.AlignCenter) self.set_column_width() # Set properties header lines vh = self.verticalHeader() vh.setDefaultSectionSize(25) # height lines # text alignment centered vh.setDefaultAlignment(Qt.AlignCenter) vh.setVisible(True) # Height of rows - as in the main widget self.frozen_table_view.verticalHeader(). \ setDefaultSectionSize( vh.defaultSectionSize() ) # Show frozen table view self.frozen_table_view.show() # Set the size of him like the main self.setHorizontalScrollMode( QAbstractItemView.ScrollPerPixel ) self.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel ) self.frozen_table_view.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel ) ## select the first column (STR Type) self.frozen_table_view.selectColumn(0) self.frozen_table_view.setEditTriggers( QAbstractItemView.AllEditTriggers ) self.set_size() self.signals()
def index(self, row, column, parent): new_idx = QSortFilterProxyModel.index(self, row, self.LAYER_COL, parent) if column == self.LAYER_COL: return new_idx idx = self.createIndex(row, column, new_idx.internalId()) return idx
class NafiDockWidget(QtWidgets.QDockWidget, Ui_NafiDockWidgetBase): closingPlugin = pyqtSignal() def __init__(self, parent=None): """Constructor.""" super(NafiDockWidget, self).__init__(parent) self.setupUi(self) # set up QTreeView self.treeView.setHeaderHidden(True) self.treeView.setSortingEnabled(True) self.treeView.setFocusPolicy(Qt.NoFocus) self.treeView.pressed.connect(self.treeViewPressed) # set up search signal self.lineEdit.textChanged.connect(self.searchTextChanged) self.searchText = "" # set up clear search self.clearSearchButton.clicked.connect(self.clearSearch) # set up About … dialog self.aboutButton.clicked.connect(self.showAboutDialog) # set up Download NAFI data … link button self.dataButton.clicked.connect( lambda: webbrowser.open(getNafiDataUrl())) # set up base model self.treeViewModel = NafiTreeViewModel(getNafiUrl()) # set up proxy model for filtering self.proxyModel = QSortFilterProxyModel(self.treeView) self.proxyModel.setSourceModel(self.treeViewModel) self.proxyModel.setRecursiveFilteringEnabled(True) self.treeView.setModel(self.proxyModel) self.reader = NafiCapabilitiesReader() self.reader.capabilitiesDownloaded.connect( lambda xml: self.initModel(xml)) # restore the view from source whenever this dock widget is made visible again self.visibilityChanged.connect( lambda visible: visible and self.loadNafiWms()) # initialise proxied tree view model from WMS contents # self.loadNafiWms() def loadNafiWms(self): """Load the NAFI WMS and additional layers.""" self.wmsUrl = getNafiUrl() self.reader.downloadCapabilities(self.wmsUrl) def initModel(self, wmsXml): """Initialise a QStandardItemModel from the NAFI WMS.""" googSat = GoogleXyzItem() googHyb = GoogleXyzItem("y") googStr = GoogleXyzItem("m") # ibraWms = IbraWmsItem() ozTopoWmts = OzTopoWmtsItem() self.treeViewModel.loadWms( self.wmsUrl, wmsXml, additionalItems=[googSat, googHyb, googStr, ozTopoWmts]) # set default sort and expansion self.proxyModel.sort(0, Qt.AscendingOrder) self.expandTopLevel() def expandTopLevel(self): # expand the top level items for row in range(self.proxyModel.rowCount()): self.treeView.expand(self.proxyModel.index(row, 0)) def treeViewPressed(self, index): """Load a NAFI WMS layer given an index in the tree view.""" assert isinstance( index, QModelIndex), "Supplied parameter is not a QModelIndex" realIndex = self.proxyModel.mapToSource(index) modelNode = self.treeViewModel.itemFromIndex(realIndex) # if we've got a layer and not a layer group, add to map if modelNode is not None: if isinstance( modelNode, (GoogleXyzItem, IbraWmsItem, OzTopoWmtsItem, WmsItem)): modelNode.addLayer() def searchTextChanged(self, text): """Process a change in the search filter text.""" # user adding characters and has exceeded 3 or more, or is removing characters if len(text) >= 3 or len(self.searchText) > len(text): regex = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxyModel.setFilterRegExp(regex) self.treeView.expandAll() # update last search text state self.searchText = text def clearSearch(self): """Clear search data.""" self.lineEdit.setText(None) self.treeView.collapseAll() def sizeHint(self): return QtCore.QSize(150, 400) def showAboutDialog(self): """Show an About … dialog.""" aboutDialog = NafiAboutDialog() aboutDialog.exec_() def closeEvent(self, event): """Handle plug-in close.""" self.closingPlugin.emit() event.accept()
def parent(self, child): return QSortFilterProxyModel.parent(self, self.createIndex(child.row(), self.LAYER_COL, child.internalId()))