From c890c59bcac214d9029691240de5b19f181a954b Mon Sep 17 00:00:00 2001 From: "benjamin.jakimow" <benjamin.jakimow@geo.hu-berlin.de> Date: Fri, 25 May 2018 16:00:51 +0200 Subject: [PATCH] spectrallibraries.py SpectralLibraryDialog now with QToolBar, added AddAttributeDialog(QDialog) --- test/test_spectrallibraries.py | 13 + timeseriesviewer/spectrallibraries.py | 494 ++++++++++++--- timeseriesviewer/ui/spectrallibrarywidget.ui | 567 ++++++++++-------- timeseriesviewer/ui/template_toolbardialog.ui | 99 +++ 4 files changed, 827 insertions(+), 346 deletions(-) create mode 100644 timeseriesviewer/ui/template_toolbardialog.ui diff --git a/test/test_spectrallibraries.py b/test/test_spectrallibraries.py index 22c053c2..13e8ac01 100644 --- a/test/test_spectrallibraries.py +++ b/test/test_spectrallibraries.py @@ -78,6 +78,19 @@ class TestInit(unittest.TestCase): self.assertEqual(f.typeName(), 'double') + def test_AttributeDialog(self): + + speclib = self.createSpeclib() + + d = AddAttributeDialog(speclib) + d.exec_() + + if d.result() == QDialog.Accepted: + field = d.field() + self.assertIsInstance(field, QgsField) + s = "" + s = "" + def test_spectralprofile(self): diff --git a/timeseriesviewer/spectrallibraries.py b/timeseriesviewer/spectrallibraries.py index cbc9c800..44d3cb5c 100644 --- a/timeseriesviewer/spectrallibraries.py +++ b/timeseriesviewer/spectrallibraries.py @@ -116,6 +116,152 @@ def value2str(value, sep=' '): value = str(value) return value + +class AddAttributeDialog(QDialog): + + def __init__(self, layer, parent=None): + assert isinstance(layer, QgsVectorLayer) + super(AddAttributeDialog, self).__init__(parent) + + assert isinstance(layer, QgsVectorLayer) + self.mLayer = layer + + self.setWindowTitle('Add Field') + l = QGridLayout() + + self.tbName = QLineEdit('Name') + self.tbName.setPlaceholderText('Name') + self.tbName.textChanged.connect(self.validate) + + l.addWidget(QLabel('Name'), 0,0) + l.addWidget(self.tbName, 0, 1) + + self.tbComment = QLineEdit() + self.tbComment.setPlaceholderText('Comment') + l.addWidget(QLabel('Comment'), 1, 0) + l.addWidget(self.tbComment, 1, 1) + + self.cbType = QComboBox() + self.typeModel = OptionListModel() + + for ntype in self.mLayer.dataProvider().nativeTypes(): + assert isinstance(ntype, QgsVectorDataProvider.NativeType) + o = Option(ntype,name=ntype.mTypeName, tooltip=ntype.mTypeDesc) + self.typeModel.addOption(o) + self.cbType.setModel(self.typeModel) + self.cbType.currentIndexChanged.connect(self.onTypeChanged) + l.addWidget(QLabel('Type'), 2, 0) + l.addWidget(self.cbType, 2, 1) + + self.sbLength = QSpinBox() + self.sbLength.setRange(0, 99) + self.sbLength.valueChanged.connect(lambda : self.setPrecisionMinMax()) + self.lengthLabel = QLabel('Length') + l.addWidget(self.lengthLabel, 3, 0) + l.addWidget(self.sbLength, 3, 1) + + self.sbPrecision = QSpinBox() + self.sbPrecision.setRange(0, 99) + self.precisionLabel = QLabel('Precision') + l.addWidget(self.precisionLabel, 4, 0) + l.addWidget(self.sbPrecision, 4, 1) + + self.tbValidationInfo = QLabel() + self.tbValidationInfo.setStyleSheet("QLabel { color : red}") + l.addWidget(self.tbValidationInfo, 5, 0, 1, 2) + + + self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + self.buttons.button(QDialogButtonBox.Ok).clicked.connect(self.accept) + self.buttons.button(QDialogButtonBox.Cancel).clicked.connect(self.reject) + l.addWidget(self.buttons, 6, 1) + self.setLayout(l) + + self.mLayer = layer + + self.onTypeChanged() + + def accept(self): + + msg = self.validate() + + if len(msg) > 0: + QMessageBox.warning(self, "Add Field", msg) + else: + super(AddAttributeDialog, self).accept() + + def field(self): + """ + Returns the new QgsField + :return: + """ + ntype = self.currentNativeType() + return QgsField(name=self.tbName.text(), + type=QVariant(ntype.mType).type(), + typeName=ntype.mTypeName, + len=self.sbLength.value(), + prec=self.sbPrecision.value(), + comment=self.tbComment.text()) + + + + + def currentNativeType(self): + return self.cbType.currentData().value() + + def onTypeChanged(self, *args): + ntype = self.currentNativeType() + vMin , vMax = ntype.mMinLen, ntype.mMaxLen + assert isinstance(ntype, QgsVectorDataProvider.NativeType) + + isVisible = vMin < vMax + self.sbLength.setVisible(isVisible) + self.lengthLabel.setVisible(isVisible) + self.setSpinBoxMinMax(self.sbLength, vMin , vMax) + self.setPrecisionMinMax() + + def setPrecisionMinMax(self): + ntype = self.currentNativeType() + vMin, vMax = ntype.mMinPrec, ntype.mMaxPrec + isVisible = vMin < vMax + self.sbPrecision.setVisible(isVisible) + self.precisionLabel.setVisible(isVisible) + + vMax = max(ntype.mMinPrec, min(ntype.mMaxPrec, self.sbLength.value())) + self.setSpinBoxMinMax(self.sbPrecision, vMin, vMax) + + def setSpinBoxMinMax(self, sb, vMin, vMax): + assert isinstance(sb, QSpinBox) + value = sb.value() + sb.setRange(vMin, vMax) + + if value > vMax: + sb.setValue(vMax) + elif value < vMin: + sb.setValue(vMin) + + + def validate(self): + + msg = [] + name = self.tbName.text() + if name in self.mLayer.fields().names(): + msg.append('Field name "{}" already exists.'.format(name)) + elif name == '': + msg.append('Missing field name') + elif name == 'shape': + msg.append('Field name "{}" already reserved.'.format(name)) + + msg = '\n'.join(msg) + self.buttons.button(QDialogButtonBox.Ok).setEnabled(len(msg) == 0) + + self.tbValidationInfo.setText(msg) + + return msg + + + + class SpectralLibraryTableFilterModel(QgsAttributeTableFilterModel): def __init__(self, sourceModel, parent=None): @@ -136,12 +282,13 @@ class SpectralLibraryTableView(QgsAttributeTableView): super(SpectralLibraryTableView, self).__init__(parent) - #self.setSelectionBehavior(QAbstractItemView.SelectItems) + #self.setSelectionBehavior(QAbstractItemView.SelectRows) #self.setSelectionMode(QAbstractItemView.SingleSelection) self.willShowContextMenu.connect(self.onWillShowContextMenu) #self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) + self.mSelectionManager = None def setModel(self, filterModel): @@ -151,8 +298,11 @@ class SpectralLibraryTableView(QgsAttributeTableView): self.mSelectionManager = SpectralLibraryFeatureSelectionManager(self.model().layer()) self.setFeatureSelectionManager(self.mSelectionManager) + self.selectionModel().selectionChanged.connect(self.onSelectionChanged) - + def onSelectionChanged(self, selected, deselected): + s = "" + r = "" #def contextMenuEvent(self, event): def onWillShowContextMenu(self, menu, index): assert isinstance(menu, QMenu) @@ -163,8 +313,10 @@ class SpectralLibraryTableView(QgsAttributeTableView): if not isinstance(featureIDs, list): s = "" + + if len(featureIDs) > 0: - m = menu.addMenu('Copy ...') + m = menu.addMenu('Copy...') a = m.addAction("Values") a.triggered.connect(lambda b, ids=featureIDs, mode=ClipboardIO.WritingModes.VALUES: self.onCopy2Clipboard(ids, mode)) a = m.addAction("Attributes") @@ -172,18 +324,27 @@ class SpectralLibraryTableView(QgsAttributeTableView): a = m.addAction("Values + Attributes") a.triggered.connect(lambda b, ids=featureIDs, mode=ClipboardIO.WritingModes.ALL: self.onCopy2Clipboard(ids, mode)) - a = menu.addAction('Save...') + a = menu.addAction('Save as...') a.triggered.connect(lambda b, ids=featureIDs : self.onSaveToFile(ids)) menu.addSeparator() - a = menu.addAction('Style...') + a = menu.addAction('Set Style') a.triggered.connect(lambda b, ids=featureIDs : self.onSetStyle(ids)) - a = menu.addAction('Show profiles') - a.triggered.connect(lambda : self.setCheckState(featureIndices, Qt.Checked)) - a = menu.addAction('Hide profiles') - a.triggered.connect(lambda: self.setCheckState(featureIndices, Qt.Unchecked)) - a = menu.addAction('Remove profiles') + a = menu.addAction('Check') + a.triggered.connect(lambda : self.setCheckState(featureIDs, Qt.Checked)) + a = menu.addAction('Uncheck') + a.triggered.connect(lambda: self.setCheckState(featureIDs, Qt.Unchecked)) + menu.addSeparator() + a = menu.addAction('Remove') a.triggered.connect(lambda : self.onRemoveFIDs(featureIDs)) + if self.spectralLibrary().isEditable(): + a = menu.addAction('Save edits') + a.triggered.connect(lambda : self.spectralLibrary().commitChanges()) + else: + a = menu.addAction('Start editing..') + a.triggered.connect(lambda: self.spectralLibrary().startEditing()) + + def spectralLibrary(self): return self.model().layer() @@ -223,35 +384,75 @@ class SpectralLibraryTableView(QgsAttributeTableView): speclib = self.spectralLibrary() assert isinstance(speclib, SpectralLibrary) - b = speclib.isEditable() - if not b: - speclib.startEditing() + wasEditable = speclib.isEditable() + speclib.startEditing() speclib.deleteFeatures(fids) - if not b: + speclib.commitChanges() + if wasEditable: + speclib.startEditing() + + def onSetStyle(self, ids): + + if len(ids) == 0: + return + + speclib = self.spectralLibrary() + assert isinstance(speclib, SpectralLibrary) + + profiles = speclib.profiles(ids) + refProfile = profiles[0] + styleDefault = refProfile.style() + refStyle = PlotStyleDialog.getPlotStyle(plotStyle=styleDefault) + refProfile.setStyle(refStyle) + + iStyle = speclib.fields().indexFromName(HIDDEN_ATTRIBUTE_PREFIX+'style') + assert iStyle >= 0 + + + if isinstance(refStyle, PlotStyle): + + wasEditable = speclib.isEditable() + speclib.startEditing() + for f in profiles: + assert isinstance(f, SpectralProfile) + oldStyle = f.style() + refStyle.setVisibility(oldStyle.isVisible()) + speclib.changeAttributeValue(f.id(), iStyle, pickle.dumps(refStyle), f.attributes()[iStyle]) speclib.commitChanges() + if wasEditable: + speclib.editingStarted() - def onSetStyle(self, featureIDs): - fmodel = self.model() - indices = self.selectedFeatureIndices() - if len(indices) > 0: - styleDefault = fmodel.data(indices[0], role=Qt.DecorationRole) - if not isinstance(styleDefault, PlotStyle): - styleDefault = None + def setCheckState(self, fids, checkState): - style = PlotStyleDialog.getPlotStyle(plotStyle=styleDefault) - if isinstance(style, PlotStyle): - for idx in indices: - fmodel.setData(idx, style, Qt.DecorationRole) + speclib = self.spectralLibrary() + speclib.startEditing() - def setCheckState(self, indices, checkState): - fmodel = self.model() + profiles = speclib.profiles(fids) + + wasEditable = speclib.isEditable() - for idx in indices: - fmodel.setData(idx, checkState, Qt.CheckStateRole) + speclib.startEditing() + iStyle = speclib.fields().indexFromName(HIDDEN_ATTRIBUTE_PREFIX + 'style') + + setVisible = checkState == Qt.Checked + for p in profiles: + assert isinstance(p, SpectralProfile) + oldStyle = p.style() + assert isinstance(oldStyle, PlotStyle) + + if oldStyle.isVisible() != setVisible: + newStyle = p.style() + newStyle.setVisibility(setVisible) + p.setStyle(newStyle) + speclib.changeAttributeValue(p.id(), iStyle, p.attributes()[iStyle], oldStyle) + speclib.commitChanges() + + if wasEditable: + speclib.editingStarted() def dropEvent(self, event): @@ -1482,21 +1683,36 @@ class SpectralLibrary(QgsVectorLayer): if not inEditMode: self.commitChanges() - def profiles(self): - return self[:] - def speclibFromFeatureIDs(self, fids): - if not isinstance(fids, list): - fids = [fids] - for id in fids: - assert isinstance(id, int) + def features(self, fids=None): + """ + Returns the QgsFeatures stored in this QgsVectorLayer + :param fids: optional, [int-list-of-feature-ids] to return + :return: [List-of-QgsFeatures] + """ featureRequest = QgsFeatureRequest() - featureRequest.setFilterFids(fids) - features = list(self.getFeatures(featureRequest)) + if fids is not None: + if not isinstance(fids, list): + fids = list(fids) + featureRequest.setFilterFids(fids) + # features = [f for f in self.features() if f.id() in fids] + return list(self.getFeatures(featureRequest)) + + + def profiles(self, fids=None): + """ + Like features(fids=None), but converts each returned QgsFeature into a SpectralProfile + :param fids: optional, [int-list-of-feature-ids] to return + :return: [List-of-SpectralProfiles] + """ + return [SpectralProfile.fromSpecLibFeature(f) for f in self.features(fids=fids)] + + + + def speclibFromFeatureIDs(self, fids): sp = SpectralLibrary(fields=self.fields()) - profiles = [SpectralProfile.fromSpecLibFeature(f) for f in features] - sp.addProfiles(profiles) + sp.addProfiles(self.profiles(fids)) return sp def groupBySpectralProperties(self): @@ -1602,7 +1818,7 @@ class SpectralLibrary(QgsVectorLayer): yield SpectralProfile.fromSpecLibFeature(f) def __getitem__(self, slice): - features = list(self.getFeatures())[slice] + features = self.features()[slice] if isinstance(features, list): return [SpectralProfile.fromSpecLibFeature(f) for f in features] else: @@ -1679,15 +1895,27 @@ class SpectralLibraryTableModel(QgsAttributeTableModel): return SpectralProfile.fromSpecLibFeature(feature) def data(self, index, role=Qt.DisplayRole): + """ + Returns Spectral Library data + Use column = 0 and Qt.CheckStateRole to return PlotStyle visibility + Use column = 0 and Qt.DecorationRole to return QIcon with PlotStyle preview + Use column = 0 and Qt.UserRole to return entire PlotStyle + :param index: QModelIndex + :param role: enum Qt.ItemDataRole + :return: value + """ if role is None or not index.isValid(): return None result = super(SpectralLibraryTableModel,self).data(index, role=role) - if index.column() == 0 and role in [Qt.CheckStateRole, Qt.DecorationRole]: + if index.column() == 0 and role in [Qt.CheckStateRole, Qt.DecorationRole, Qt.UserRole]: profile = self.spectralProfile(index) style = profile.style() assert isinstance(style, PlotStyle) + if role == Qt.UserRole: + result = style + if role == Qt.CheckStateRole: result = Qt.Checked if style.isVisible() else Qt.Unchecked @@ -1705,27 +1933,41 @@ class SpectralLibraryTableModel(QgsAttributeTableModel): def setData(self, index, value, role=None): + """ + Sets the Speclib data + use column 0 and Qt.CheckStateRole to PlotStyle visibility + use column 0 and Qt.UserRole to set PlotStyle + + :param index: QModelIndex() + :param value: value to set + :param role: role + :return: True | False + """ if role is None or not index.isValid(): return False result = False speclib = self.layer() assert isinstance(speclib, SpectralLibrary) - if index.column() == 0 and role in [Qt.CheckStateRole, Qt.DecorationRole]: + if index.column() == 0 and role in [Qt.CheckStateRole, Qt.UserRole]: profile = self.spectralProfile(index) - style = profile.style() + b = speclib.isEditable() - assert isinstance(style, PlotStyle) + if role == Qt.CheckStateRole: + style = profile.style() style.setVisibility(value == Qt.Checked) profile.setStyle(style) - if role == Qt.DecorationRole and isinstance(value, PlotStyle): + + if role == Qt.UserRole and isinstance(value, PlotStyle): profile.setStyle(value) if not b: + speclib.startEditing() result = speclib.updateFeature(profile) + if not b: speclib.commitChanges() @@ -1839,11 +2081,77 @@ class SpectralLibraryPlotWidget(PlotWidget): def setModel(self, model): assert isinstance(model, SpectralLibraryTableModel) self.mModel = model - self.mModel.rowsAboutToBeRemoved.connect(self.onRowsAboutToBeRemoved) - self.mModel.rowsInserted.connect(self.onRowsInserted) - self.mModel.dataChanged.connect(self.onDataChanged) - if self.mModel.rowCount() > 0: - self.onRowsInserted(self.mModel.index(0,0), 0, self.mModel.rowCount()) + speclib = self.speclib() + assert isinstance(speclib, SpectralLibrary) + speclib.committedFeaturesAdded.connect(self.onProfilesAdded) + speclib.committedFeaturesRemoved.connect(self.onProfilesRemoved) + speclib.committedAttributeValuesChanges.connect(self.onProfileDataChanged) + + self.onProfilesAdded(speclib.id(), speclib.allFeatureIds()) + + #self.mModel.rowsAboutToBeRemoved.connect(self.onRowsAboutToBeRemoved) + #self.mModel.rowsInserted.connect(self.onRowsInserted) + #self.mModel.dataChanged.connect(self.onDataChanged) + #if self.mModel.rowCount() > 0: + # self.onRowsInserted(self.mModel.index(0,0), 0, self.mModel.rowCount()) + + + def speclib(self): + if not isinstance(self.mModel, SpectralLibraryTableModel): + return None + return self.mModel.speclib() + + def onProfileDataChanged(self, layerID, changeMap): + + + fieldNames = self.speclib().fieldNames() + iStyle = fieldNames.index(HIDDEN_ATTRIBUTE_PREFIX+'style') + + fids = list(changeMap.keys()) + for fid in fids: + style = changeMap[fid].get(iStyle) + style = pickle.loads(style) + + pdi = self.mPlotItems.get(fid) + if isinstance(pdi, SpectralProfilePlotDataItem): + pdi.setStyle(style) + + + s = "" + + def onProfilesAdded(self, layerID, features): + + if len(features) == 0: + return + + speclib = self.speclib() + assert isinstance(speclib, SpectralLibrary) + + fids = [f.id() for f in features] + #remove if existent + self.onProfilesRemoved(layerID, fids) + + pdis = [] + for feature in features: + profile = SpectralProfile.fromSpecLibFeature(feature) + assert isinstance(profile, SpectralProfile) + pdi = SpectralProfilePlotDataItem(profile) + self.mPlotItems[pdi.mProfile.id()] = pdi + pdis.append(pdi) + + for pdi in pdis: + self.plotItem.addItem(pdi) + + + def onProfilesRemoved(self, layerID, fids): + + if len(fids) == 0: + return + fids = [fid for fid in fids if fid in list(self.mPlotItems.keys())] + pdis = [self.mPlotItems.pop(fid) for fid in fids] + pdis = [pdi for pdi in pdis if isinstance(pdi, SpectralProfilePlotDataItem)] + for pdi in pdis: + self.removeItem(pdi) def onDataChanged(self, topLeft, bottomRight, roles): @@ -1903,26 +2211,6 @@ class SpectralLibraryPlotWidget(PlotWidget): self.mSpeclib.addSpeclib(speclib) event.accept() - def onProfilesAdded(self, profiles): - # todo: remove some PDIs from plot if there are too many - pi = self.getPlotItem() - if True: - to_remove = max(0, len(pi.listDataItems()) - self.m_plot_max) - if to_remove > 0: - for pdi in pi.listDataItems()[0:to_remove]: - pi.removeItem(pdi) - - for p in profiles: - self.mPlotXUnitModel.addOption(Option(p.xUnit())) - pi.addItem(self.createPDI(p)) - - self.btnRemoveAttribute.setEnabled(len(self.mSpeclib.metadataAttributes()) > 0) - - def onProfilesRemoved(self, profiles): - for p in profiles: - self.removePDI(p) - self.btnRemoveAttribute.setEnabled(len(self.mSpeclib.metadataAttributes()) > 0) - class SpectralLibraryWidget(QFrame, loadUI('spectrallibrarywidget.ui')): sigLoadFromMapRequest = pyqtSignal() @@ -1979,25 +2267,35 @@ class SpectralLibraryWidget(QFrame, loadUI('spectrallibrarywidget.ui')): pi.dropEvent = self.dropEvent - self.btnLoadFromFile.clicked.connect(lambda : self.addSpeclib(SpectralLibrary.readFromSourceDialog(self))) - self.btnExportSpeclib.clicked.connect(self.onExportSpectra) + self.initActions() + self.setMapInteraction(False) - self.btnAddCurrentToSpeclib.clicked.connect(self.addCurrentSpectraToSpeclib) + def initActions(self): - self.btnLoadfromMap.clicked.connect(self.sigLoadFromMapRequest.emit) + self.actionSelectProfilesFromMap.triggered.connect(self.sigLoadFromMapRequest.emit) + self.actionSaveCurrentProfiles + self.actionImportSpeclib + self.actionSaveSpeclib - self.btnAddAttribute.clicked.connect( - lambda :self.mModel.addAttribute( - QInputDialog.getText(self, 'Add Attribute', 'Attribute', text='New Attribute')[0]) - ) + self.actionReload + self.actionToggleEditing + self.actionSaveEdits + self.actionDeleteSelected + self.actionCutSelectedRows.setVisible(False) + self.actionCopySelectedRows + self.actionPasteFeatures.setVisible(False) + + self.actionSelectAll + self.actionInvertSelection + self.actionRemoveSelection + + #to hide + self.actionPanMapToSelectedRows.setVisible(False) + self.actionZoomMapToSelectedRows.setVisible(False) - self.btnRemoveAttribute.setEnabled(len(self.mSpeclib) > 0) - self.btnRemoveAttribute.clicked.connect( - lambda : self.mModel.removeAttribute( - QInputDialog.getItem(self, 'Delete Attribute', 'Attributes', - self.mModel.mAttributeColumns, editable=False)[0] - ) - ) + + self.actionAddAttribute.triggered.connect() + self.actionRemoveAttribute.triggered.connect() def updateTableConfig(self): @@ -2016,11 +2314,16 @@ class SpectralLibraryWidget(QFrame, loadUI('spectrallibrarywidget.ui')): self.mFilterModel.setAttributeTableConfig(self.mTableConfig) #self.tableViewSpeclib.setAttributeTableConfig(self.mTableConfig) - def setMapInteraction(self, b): - assert isinstance(b, bool) - if b is None or b is False: + def setMapInteraction(self, b : bool): + + if b == False: self.setCurrentSpectra(None) - self.btnBoxMapInteraction.setEnabled(b) + + self.actionSelectProfilesFromMap.setVisible(b) + self.actionSaveCurrentProfiles.setVisible(b) + self.actionPanMapToSelectedRows.setVisbible(b) + self.actionZoomMapToSelectedRows.setVisbible(b) + def mapInteraction(self): return self.btnBoxMapInteraction.isEnabled() @@ -2214,11 +2517,20 @@ class SpectralLibraryFeatureSelectionManager(QgsIFeatureSelectionManager): return self.mLayer def deselect(self, ids): - self.mLayer.deselect(ids) + + if len(ids) > 0: + selected = [id for id in self.selectedFeatureIds() if id not in ids] + self.mLayer.deselect(ids) + + self.selectionChanged.emit(selected, ids, True) def select(self, ids): self.mLayer.select(ids) + def selectFeatures(self, selection, command): + + super(SpectralLibraryFeatureSelectionManager, self).selectF + s = "" def selectedFeatureCount(self): return self.mLayer.selectedFeatureCount() diff --git a/timeseriesviewer/ui/spectrallibrarywidget.ui b/timeseriesviewer/ui/spectrallibrarywidget.ui index 013c6dfa..415c37dc 100644 --- a/timeseriesviewer/ui/spectrallibrarywidget.ui +++ b/timeseriesviewer/ui/spectrallibrarywidget.ui @@ -36,265 +36,40 @@ <number>0</number> </property> <item> - <widget class="QFrame" name="btnBox"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>30</height> - </size> + <widget class="QToolBar" name="mToolbar"> + <property name="styleSheet"> + <string notr="true"/> </property> - <property name="maximumSize"> + <property name="iconSize"> <size> - <width>16777215</width> - <height>26</height> + <width>18</width> + <height>18</height> </size> </property> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> + <property name="floatable"> + <bool>false</bool> </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QFrame" name="btnBoxMapInteraction"> - <property name="minimumSize"> - <size> - <width>50</width> - <height>0</height> - </size> - </property> - <property name="frameShape"> - <enum>QFrame::NoFrame</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QToolButton" name="btnLoadfromMap"> - <property name="toolTip"> - <string>Select a spectrum from a map.</string> - </property> - <property name="styleSheet"> - <string notr="true"/> - </property> - <property name="text"> - <string>Select from map</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/pickrasterspectrum.svg</normaloff>:/timeseriesviewer/icons/pickrasterspectrum.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnAddCurrentToSpeclib"> - <property name="toolTip"> - <string>Add the current spectrum to the spectral library.</string> - </property> - <property name="text"> - <string>Add last profile to spectral library</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/profile2speclib.svg</normaloff>:/timeseriesviewer/icons/profile2speclib.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="checkable"> - <bool>false</bool> - </property> - <property name="checked"> - <bool>false</bool> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="cbAddCurrentSpectraToSpeclib"> - <property name="font"> - <font> - <pointsize>10</pointsize> - </font> - </property> - <property name="toolTip"> - <string>Check to add current spectra automatically to the spectral library.</string> - </property> - <property name="text"> - <string>Add Profiles</string> - </property> - <property name="checkable"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QToolButton" name="btnLoadFromFile"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="toolTip"> - <string>Load spectral library.</string> - </property> - <property name="text"> - <string>Import</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/speclib_add.svg</normaloff>:/timeseriesviewer/icons/speclib_add.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnExportSpeclib"> - <property name="toolTip"> - <string>Save the spectral library to file.</string> - </property> - <property name="text"> - <string>Export</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/speclib_save.svg</normaloff>:/timeseriesviewer/icons/speclib_save.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnAddAttribute"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Add attribute</string> - </property> - <property name="text"> - <string>Add Attribute</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/add_class.svg</normaloff>:/timeseriesviewer/icons/add_class.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnRemoveAttribute"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Remove attribute</string> - </property> - <property name="text"> - <string>Remove Attribute</string> - </property> - <property name="icon"> - <iconset resource="resources.qrc"> - <normaloff>:/timeseriesviewer/icons/remove_class.svg</normaloff>:/timeseriesviewer/icons/remove_class.svg</iconset> - </property> - <property name="iconSize"> - <size> - <width>32</width> - <height>32</height> - </size> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> + <addaction name="actionSelectProfilesFromMap"/> + <addaction name="actionSaveCurrentProfiles"/> + <addaction name="actionImportSpeclib"/> + <addaction name="actionSaveSpeclib"/> + <addaction name="actionReload"/> + <addaction name="separator"/> + <addaction name="actionToggleEditing"/> + <addaction name="actionSaveEdits"/> + <addaction name="actionDeleteSelected"/> + <addaction name="actionCutSelectedRows"/> + <addaction name="actionCopySelectedRows"/> + <addaction name="actionPasteFeatures"/> + <addaction name="separator"/> + <addaction name="actionSelectAll"/> + <addaction name="actionInvertSelection"/> + <addaction name="actionRemoveSelection"/> + <addaction name="actionPanMapToSelectedRows"/> + <addaction name="actionZoomMapToSelectedRows"/> + <addaction name="separator"/> + <addaction name="actionAddAttribute"/> + <addaction name="actionRemoveAttribute"/> </widget> </item> <item> @@ -332,7 +107,11 @@ <number>0</number> </property> <item> - <widget class="SpectralLibraryPlotWidget" name="plotWidget"/> + <widget class="SpectralLibraryPlotWidget" name="plotWidget"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + </widget> </item> <item> <widget class="QFrame" name="btnBarPlotOptions"> @@ -430,6 +209,9 @@ <property name="acceptDrops"> <bool>true</bool> </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> @@ -458,6 +240,279 @@ </widget> </item> </layout> + <action name="actionImportSpeclib"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/speclib_add.svg</normaloff>:/timeseriesviewer/icons/speclib_add.svg</iconset> + </property> + <property name="text"> + <string>importSpeclib</string> + </property> + <property name="toolTip"> + <string>Import Spectral Library</string> + </property> + </action> + <action name="actionSaveSpeclib"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/speclib_save.svg</normaloff>:/timeseriesviewer/icons/speclib_save.svg</iconset> + </property> + <property name="text"> + <string>Save Spectral Library</string> + </property> + <property name="toolTip"> + <string>Save Profiles in Spectral Library</string> + </property> + </action> + <action name="mActionCopySelectedRows"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionEditCopy.svg</normaloff>:/images/themes/default/mActionEditCopy.svg</iconset> + </property> + <property name="text"> + <string>Copy selected rows to clipboard</string> + </property> + <property name="toolTip"> + <string>Copy selected rows to clipboard (Ctrl+C)</string> + </property> + <property name="shortcut"> + <string>Ctrl+C</string> + </property> + </action> + <action name="actionAddAttribute"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionNewAttribute.svg</normaloff>:/images/themes/default/mActionNewAttribute.svg</iconset> + </property> + <property name="text"> + <string>New field</string> + </property> + <property name="toolTip"> + <string>New field (Ctrl+W)</string> + </property> + <property name="shortcut"> + <string>Ctrl+W</string> + </property> + </action> + <action name="actionPasteFeatures"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionEditPaste.svg</normaloff>:/images/themes/default/mActionEditPaste.svg</iconset> + </property> + <property name="text"> + <string>Paste features from clipboard</string> + </property> + <property name="toolTip"> + <string>Paste features from clipboard (Ctrl+V)</string> + </property> + <property name="shortcut"> + <string>Ctrl+V</string> + </property> + </action> + <action name="actionRemoveAttribute"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionDeleteAttribute.svg</normaloff>:/images/themes/default/mActionDeleteAttribute.svg</iconset> + </property> + <property name="text"> + <string>Delete field</string> + </property> + <property name="toolTip"> + <string>Delete field (Ctrl+L)</string> + </property> + <property name="shortcut"> + <string>Ctrl+L</string> + </property> + </action> + <action name="actionSaveEdits"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionSaveAllEdits.svg</normaloff>:/images/themes/default/mActionSaveAllEdits.svg</iconset> + </property> + <property name="text"> + <string>Save edits</string> + </property> + <property name="toolTip"> + <string>Save edits (Ctrl+S)</string> + </property> + <property name="shortcut"> + <string>Ctrl+S</string> + </property> + </action> + <action name="actionToggleEditing"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionToggleEditing.svg</normaloff>:/images/themes/default/mActionToggleEditing.svg</iconset> + </property> + <property name="text"> + <string>Toggle editing mode</string> + </property> + <property name="toolTip"> + <string>Toggle editing mode (Ctrl+E)</string> + </property> + <property name="shortcut"> + <string>Ctrl+E</string> + </property> + </action> + <action name="actionSelectProfilesFromMap"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/pickrasterspectrum.svg</normaloff>:/timeseriesviewer/icons/pickrasterspectrum.svg</iconset> + </property> + <property name="text"> + <string>Select Profiles from Map</string> + </property> + <property name="toolTip"> + <string>Select new profile from map</string> + </property> + </action> + <action name="actionSaveCurrentProfiles"> + <property name="icon"> + <iconset resource="resources.qrc"> + <normaloff>:/timeseriesviewer/icons/profile2speclib.svg</normaloff>:/timeseriesviewer/icons/profile2speclib.svg</iconset> + </property> + <property name="text"> + <string>Save current profiles</string> + </property> + <property name="toolTip"> + <string>Add current profile(s) to spectral library</string> + </property> + </action> + <action name="actionReload"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionDraw.svg</normaloff>:/images/themes/default/mActionDraw.svg</iconset> + </property> + <property name="text"> + <string>Reload the table</string> + </property> + <property name="toolTip"> + <string>Reload the table</string> + </property> + </action> + <action name="actionSelectAll"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionSelectAll.svg</normaloff>:/images/themes/default/mActionSelectAll.svg</iconset> + </property> + <property name="text"> + <string>Select all</string> + </property> + <property name="toolTip"> + <string>Select all (Ctrl+A)</string> + </property> + <property name="shortcut"> + <string>Ctrl+A</string> + </property> + </action> + <action name="actionInvertSelection"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionInvertSelection.svg</normaloff>:/images/themes/default/mActionInvertSelection.svg</iconset> + </property> + <property name="text"> + <string>Invert selection</string> + </property> + <property name="toolTip"> + <string>Invert selection (Ctrl+R)</string> + </property> + <property name="shortcut"> + <string>Ctrl+R</string> + </property> + </action> + <action name="actionRemoveSelection"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionDeselectAll.svg</normaloff>:/images/themes/default/mActionDeselectAll.svg</iconset> + </property> + <property name="text"> + <string>Deselect all</string> + </property> + <property name="toolTip"> + <string>Deselect all (Ctrl+Shift+A)</string> + </property> + <property name="shortcut"> + <string>Ctrl+Shift+A</string> + </property> + </action> + <action name="actionPanMapToSelectedRows"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionPanToSelected.svg</normaloff>:/images/themes/default/mActionPanToSelected.svg</iconset> + </property> + <property name="text"> + <string>Pan map to the selected rows</string> + </property> + <property name="toolTip"> + <string>Pan map to the selected rows (Ctrl+P)</string> + </property> + <property name="shortcut"> + <string>Ctrl+P</string> + </property> + </action> + <action name="actionZoomMapToSelectedRows"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionZoomToSelected.svg</normaloff>:/images/themes/default/mActionZoomToSelected.svg</iconset> + </property> + <property name="text"> + <string>Zoom map to the selected rows</string> + </property> + <property name="toolTip"> + <string>Zoom map to the selected rows (Ctrl+J)</string> + </property> + <property name="shortcut"> + <string>Ctrl+J</string> + </property> + </action> + <action name="actionDeleteSelected"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionDeleteSelected.svg</normaloff>:/images/themes/default/mActionDeleteSelected.svg</iconset> + </property> + <property name="text"> + <string>Delete selected features</string> + </property> + <property name="toolTip"> + <string>Delete selected features</string> + </property> + <property name="shortcut"> + <string>Del</string> + </property> + </action> + <action name="actionCutSelectedRows"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionEditCut.svg</normaloff>:/images/themes/default/mActionEditCut.svg</iconset> + </property> + <property name="text"> + <string>Cut selected rows to clipboard</string> + </property> + <property name="toolTip"> + <string>Cut selected rows to clipboard (Ctrl+X)</string> + </property> + <property name="shortcut"> + <string>Ctrl+X</string> + </property> + </action> + <action name="actionCopySelectedRows"> + <property name="icon"> + <iconset resource="../../../../QGIS/images/images.qrc"> + <normaloff>:/images/themes/default/mActionEditCopy.svg</normaloff>:/images/themes/default/mActionEditCopy.svg</iconset> + </property> + <property name="text"> + <string>Copy selected rows to clipboard</string> + </property> + <property name="toolTip"> + <string>Copy selected rows to clipboard (Ctrl+C)</string> + </property> + <property name="shortcut"> + <string>Ctrl+C</string> + </property> + </action> </widget> <customwidgets> <customwidget> @@ -473,6 +528,8 @@ </customwidgets> <resources> <include location="resources.qrc"/> + <include location="../../images/images.qrc"/> + <include location="../../../../QGIS/images/images.qrc"/> </resources> <connections/> </ui> diff --git a/timeseriesviewer/ui/template_toolbardialog.ui b/timeseriesviewer/ui/template_toolbardialog.ui new file mode 100644 index 00000000..69c941a2 --- /dev/null +++ b/timeseriesviewer/ui/template_toolbardialog.ui @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QToolBar" name="mToolbar"> + <property name="styleSheet"> + <string notr="true">background-color: rgb(85, 170, 0);</string> + </property> + <property name="iconSize"> + <size> + <width>18</width> + <height>18</height> + </size> + </property> + <property name="floatable"> + <bool>false</bool> + </property> + <addaction name="actionAction"/> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + <action name="actionAction"> + <property name="text"> + <string>Action</string> + </property> + </action> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> -- GitLab