diff --git a/Makefile b/Makefile index 8cc1e14..fe52acf 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ default: make -C specdal/gui/pyqt - pip uninstall -y specdal - pip install . + python setup.py install clean: pip uninstall SpecDAL diff --git a/setup.py b/setup.py index bb626f3..88c7d4a 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ 'bin/specdal_pipeline', 'bin/specdal_info', 'bin/specdal_gui', + 'bin/specdalqt', ], entry_points={ 'gui_scripts': ['specdal_gui = specdal.gui.viewer:main'], diff --git a/specdal/gui/pyqt/collection_plotter.py b/specdal/gui/pyqt/collection_plotter.py index 988ac25..9c2cd62 100644 --- a/specdal/gui/pyqt/collection_plotter.py +++ b/specdal/gui/pyqt/collection_plotter.py @@ -11,6 +11,71 @@ from matplotlib.figure import Figure from matplotlib.patches import Rectangle +def set_or_none(iterable): + if iterable is not None and not isinstance(iterable,set): + iterable = set(iterable) + return iterable + +class SpectrumArtist(): + show_flagged = True + show_unselected = True + + def __init__(self,artist): + self.artist = artist + self._flagged = False + self._selected = False + self._visible = True + self.style = '-' + self.color = 'k' + + @property + def flagged(self): + return self._flagged + + @flagged.setter + def flagged(self,value): + self._flagged = value + self.color = 'r' if self._flagged else 'k' + self._update_look() + + @property + def selected(self): + return self._selected + + @selected.setter + def selected(self,value): + self._selected = value + self.style = '--' if self._selected else '-' + self._update_look() + + @property + def visible(self): + return self._visible + + @visible.setter + def visible(self,value): + self._visible = value + if self._visible: + self.artist.set_linestyle(self.style) + else: + self.artist.set_linestyle('None') + + def _calculate_visibility(self): + visible = True + if not self.selected and not self.show_unselected: + visible = False + if self.flagged and not self.show_flagged: + visible = False + self.visible = visible + + def _update_look(self): + self._calculate_visibility() + if self.visible: + self.artist.set_color(self.color) + self.artist.set_linestyle(self.style) + + + class CollectionCanvas(FigureCanvasQTAgg): """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" @@ -32,21 +97,22 @@ def __init__(self, parent=None, width=5, height=4, dpi=100): QtWidgets.QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) - @property - def unselected_style(self): - return self._unselected_style - @unselected_style.setter - def unselected_style(self,value): - self._unselected_style = value + @property + def show_unselected(self): + pass + + @show_unselected.setter + def show_unselected(self,value): + SpectrumArtist.show_unselected = value @property - def flag_style(self): - return self._flag_style + def show_flagged(self): + pass - @flag_style.setter - def flag_style(self,value): - self._flag_style = value + @show_flagged.setter + def show_flagged(self,value): + SpectrumArtist.show_flagged = value def rectangleStartEvent(self,event): self._rect = None @@ -125,75 +191,50 @@ def suspendMouseNavigation(self): def update_selected(self,selected_keys,only_add=False): # better lookup time - if not isinstance(selected_keys,set): - selected_keys = set(selected_keys) + selected_keys = set_or_none(selected_keys) if only_add: # if we're only adding, just select for key in selected_keys: - self.artist_dict[key].set_linestyle('--') + self.artist_dict[key].selected = True else: # otherwise, unselect everything that isn't selected keys = self.artist_dict.keys() for key in keys: - if self.artist_dict[key].get_linestyle() == 'None' and \ - self.artist_dict[key].get_color() == 'r': - continue - if key in selected_keys: - self.artist_dict[key].set_linestyle('--') - else: - self.artist_dict[key].set_linestyle(self.unselected_style) + self.artist_dict[key].selected = key in selected_keys self.draw() - def add_flagged(self,flagged_keys,selected_keys=None): + def set_flagged(self,flagged_keys,selected_keys=None,flag=True): # better lookup time - if not isinstance(flagged_keys,set): - flagged_keys = set(flagged_keys) - if selected_keys is not None and not isinstance(selected_keys,set): - selected_keys = set(selected_keys) + flagged_keys = set_or_none(flagged_keys) + selected_keys = set_or_none(selected_keys) for key in flagged_keys: - if self.flag_style in 'rk': - self.artist_dict[key].set_color(self.flag_style) - if selected_keys is not None: - style = '--' if key in selected_keys else self.unselected_style - self.artist_dict[key].set_linestyle(style) - else: - self.artist_dict[key].set_color('r') - self.artist_dict[key].set_linestyle(self.flag_style) + self.artist_dict[key].flagged = flag + self.draw() + def add_flagged(self,unflagged_keys,selected_keys=None): + self.set_flagged(unflagged_keys,selected_keys,True) + def remove_flagged(self,unflagged_keys,selected_keys=None): - old_style = self.flag_style - self.flag_style = 'k' - self.add_flagged(unflagged_keys,selected_keys) - self.flag_style = old_style + self.set_flagged(unflagged_keys,selected_keys,False) def update_artists(self,collection,new_lim=False): if collection is None: return - #update values being plotted -> redo statistics - self.mean_line = None - self.median_line = None - self.max_line = None - self.min_line = None - self.std_line = None # save limits if new_lim == False: xlim = self.ax.get_xlim() ylim = self.ax.get_ylim() # plot self.ax.clear() - flag_style = self.flag_style - unselected_style = self.unselected_style - flags = [s.name in collection.flags for s in collection.spectra] - collection.plot(ax=self.ax, - style=list(np.where(flags, flag_style, 'k')), - picker=1) + collection.plot(ax=self.ax, style='k', picker=1) #self.ax.set_title(collection.name) - keys = [s.name for s in collection.spectra] artists = self.ax.lines - self.artist_dict = {key:artist for key,artist in zip(keys,artists)} - self.colors = {key:'k' for key in keys} + self.artist_dict = {key:SpectrumArtist(artist) + for key,artist in zip(keys,artists)} + for key in collection.flags: + self.artist_dict[key].flagged = True self.ax.legend().remove() self.ax.grid(True) self.draw() @@ -271,7 +312,7 @@ def _icon_of(name,fname,description, idx=None): _icon_of("jump","icons8-jump-correct-32.png","Jump Correct") _icon_of("interpolate","icons8-interpolate-32.png","Interpolate") _icon_of("proximal","icons8-proximal-join.png","Proximal Join") - _icon_of("reset","icons8-restart-32.png","Undo Operations") + _icon_of("reset","icons8-restart-32.png","Revert Operators") self.insertSeparator(self.icons['flag']) self.insertSeparator(self.icons['operators']) diff --git a/specdal/gui/pyqt/qt_viewer.ui b/specdal/gui/pyqt/qt_viewer.ui index 542862c..e9848e7 100644 --- a/specdal/gui/pyqt/qt_viewer.ui +++ b/specdal/gui/pyqt/qt_viewer.ui @@ -74,6 +74,22 @@ 0 + + + + Select Group: + + + + + + + + -- + + + + @@ -106,32 +122,32 @@ - - + + - + 0 0 - - - - Name for Group + Name Group - - + + - + 0 0 - Group Selection + + + + Name for Selection diff --git a/specdal/gui/pyqt/qt_viewer_ui.py b/specdal/gui/pyqt/qt_viewer_ui.py index 54057b6..a7928d8 100644 --- a/specdal/gui/pyqt/qt_viewer_ui.py +++ b/specdal/gui/pyqt/qt_viewer_ui.py @@ -52,6 +52,13 @@ def setupUi(self, MainWindow): self.gridLayout = QtWidgets.QGridLayout() self.gridLayout.setContentsMargins(-1, -1, -1, 0) self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(self.layoutWidget) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 2, 0, 1, 1) + self.comboBox = QtWidgets.QComboBox(self.layoutWidget) + self.comboBox.setObjectName("comboBox") + self.comboBox.addItem("") + self.gridLayout.addWidget(self.comboBox, 2, 1, 1, 1) self.nameSelection = QtWidgets.QLineEdit(self.layoutWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -69,6 +76,14 @@ def setupUi(self, MainWindow): self.selectByName.setSizePolicy(sizePolicy) self.selectByName.setObjectName("selectByName") self.gridLayout.addWidget(self.selectByName, 0, 1, 1, 1) + self.createGroup = QtWidgets.QPushButton(self.layoutWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.createGroup.sizePolicy().hasHeightForWidth()) + self.createGroup.setSizePolicy(sizePolicy) + self.createGroup.setObjectName("createGroup") + self.gridLayout.addWidget(self.createGroup, 1, 1, 1, 1) self.groupName = QtWidgets.QLineEdit(self.layoutWidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -78,14 +93,6 @@ def setupUi(self, MainWindow): self.groupName.setText("") self.groupName.setObjectName("groupName") self.gridLayout.addWidget(self.groupName, 1, 0, 1, 1) - self.createGroup = QtWidgets.QPushButton(self.layoutWidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.createGroup.sizePolicy().hasHeightForWidth()) - self.createGroup.setSizePolicy(sizePolicy) - self.createGroup.setObjectName("createGroup") - self.gridLayout.addWidget(self.createGroup, 1, 1, 1, 1) self.verticalLayout_2.addLayout(self.gridLayout) self.onlyShowSelected = QtWidgets.QCheckBox(self.layoutWidget) self.onlyShowSelected.setObjectName("onlyShowSelected") @@ -206,11 +213,13 @@ def setupUi(self, MainWindow): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "SpecDAL GUI")) + self.label.setText(_translate("MainWindow", "Select Group:")) + self.comboBox.setItemText(0, _translate("MainWindow", "--")) self.nameSelection.setToolTip(_translate("MainWindow", "Select Spectra by Name")) self.nameSelection.setPlaceholderText(_translate("MainWindow", "Select by Name")) self.selectByName.setText(_translate("MainWindow", "Select")) - self.groupName.setPlaceholderText(_translate("MainWindow", "Name for Group")) - self.createGroup.setText(_translate("MainWindow", "Group Selection")) + self.createGroup.setText(_translate("MainWindow", "Name Group")) + self.groupName.setPlaceholderText(_translate("MainWindow", "Name for Selection")) self.onlyShowSelected.setText(_translate("MainWindow", "Only Show Selected")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuEdit.setTitle(_translate("MainWindow", "Edit")) diff --git a/specdal/gui/pyqt/viewer.py b/specdal/gui/pyqt/viewer.py index 8faf12a..cf62e50 100644 --- a/specdal/gui/pyqt/viewer.py +++ b/specdal/gui/pyqt/viewer.py @@ -231,6 +231,7 @@ def __init__(self,parent=None): self.navbar.triggered('export').connect(self._export_flags) # Operators self.navbar.triggered('operators').connect(self.openOperatorConfig) + # TODO: Implement statistic plotting self.navbar.triggered('stats').connect( lambda:self.openOperatorConfig('stats')) self.navbar.triggered('jump').connect( @@ -330,6 +331,8 @@ def _restore_dataset(self): if self._directory is not None: self._open_dataset(self._directory) # restore flags + for flag in flags: + self._collection.flag(flag) self.canvas.add_flagged(flags) # restore groups @@ -471,15 +474,14 @@ def unflagFromList(self): self.canvas.remove_flagged(self.selection_text) def toggleSelectedVisibility(self,state): - self.show_selected = not state - self.canvas.unselected_style = '-' if self.show_selected else 'None' - print(self.canvas.unselected_style) + self.show_unselected = not state + self.canvas.show_unselected = self.show_unselected if self._collection: self.canvas.update_selected(self.selection_text) def toggleFlagVisibility(self): self.show_flagged = not self.show_flagged - self.canvas.flag_style = 'r' if self.show_flagged else 'None' + self.canvas.show_flagged = self.show_flagged if self._collection: self.canvas.add_flagged(self._collection.flags,self.selection_text)