#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ GEPARD - Gepard-Enabled PARticle Detection Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für Polymerforschung Dresden e. V. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program, see COPYING. If not, see . """ from PyQt5 import QtWidgets, QtCore class PolymerTypeCheckboxes(QtWidgets.QScrollArea): PolymerCheckBoxToggled = QtCore.pyqtSignal() def __init__(self): super(PolymerTypeCheckboxes, self).__init__() self.setWidgetResizable(True) widget = QtWidgets.QWidget() self.setWidget(widget) self.layout = QtWidgets.QVBoxLayout(widget) self.checkBoxToggledSignalFunc = lambda: self.PolymerCheckBoxToggled.emit() self.createCheckBoxGroup() self.addShowAllCheckbox() self.polymerCheckBoxes = [] def createCheckBoxGroup(self): ''' Create groupbox for holding the individual checkeboxes for each polymer type :return: ''' self.checkBoxGroup = QtWidgets.QGroupBox('Display Polymer Types:') self.checkBoxGroupLayout = QtWidgets.QVBoxLayout() self.checkBoxGroup.setLayout(self.checkBoxGroupLayout) self.layout.addWidget(self.checkBoxGroup) def addShowAllCheckbox(self): ''' Adds a checkbox for toggling whether to display all polymer types in the connected plots, or only the ones that are selected. :return: ''' self.showAllCheckBox = QtWidgets.QCheckBox('Show All') self.showAllCheckBox.setChecked(True) self.showAllCheckBox.setDisabled(True) self.showAllCheckBox.stateChanged.connect(self.checkBoxToggledSignalFunc) self.checkBoxGroupLayout.addWidget(self.showAllCheckBox) def updatePolymerCheckBoxes(self, polymerTypes): ''' Takes a list of polymerTypes and recreates the polymer Checkboxes accordingly. :return: ''' lastSelectedCheckBoxNames = self.getSelectedPolymers() self.resetCheckBoxGroup() self.polymerCheckBoxes = [] for polymer in polymerTypes: newCheckBox = QtWidgets.QCheckBox(self) newCheckBox.setText(polymer) newCheckBox.stateChanged.connect(self.checkBoxToggledSignalFunc) if polymer in lastSelectedCheckBoxNames: newCheckBox.setChecked(True) self.polymerCheckBoxes.append(newCheckBox) self.checkBoxGroupLayout.addWidget(newCheckBox) self.checkBoxGroupLayout.addStretch() self.checkBoxGroup.setLayout(self.checkBoxGroupLayout) def resetCheckBoxGroup(self): self.checkBoxGroup.setParent(None) self.createCheckBoxGroup() self.addShowAllCheckbox() self.showAllCheckBox.setDisabled(False) def getSelectedPolymers(self): ''' Return list of currently selected polymers :return: ''' return [checkbox.text() for checkbox in self.polymerCheckBoxes if checkbox.isChecked()] class PolymerNavigationToolbar(QtWidgets.QGroupBox): WidgetsUpdated = QtCore.pyqtSignal() JumpToIndicatedSpec = QtCore.pyqtSignal() def __init__(self, particleContainer): super(PolymerNavigationToolbar, self).__init__() self.setTitle('Navigate through polymers') self.layout = QtWidgets.QHBoxLayout() self.setLayout(self.layout) self.setDisabled(True) self.particleContainer = particleContainer self.currentSpectrumIndex = None self.currentParticleIndex = None self.lastSpecIndex = None self.lastParticleIndex = None self.createWidgets() def createWidgets(self): self.specNumberSelector = QtWidgets.QSpinBox() self.specNumberSelector.setMinimumWidth(100) self.specNumberSelector.setMinimum(1) numSpectra = self.particleContainer.getNumberOfMeasurements() self.specNumberSelector.setMaximum(numSpectra) self.jumpToSpecBtn = QtWidgets.QPushButton('Jump To Spectrum of Number:') self.jumpToSpecBtn.released.connect(lambda: self.JumpToIndicatedSpec.emit()) self.typeSelectorCombo = QtWidgets.QComboBox() self.typeSelectorCombo.setMinimumWidth(150) self.particleSelector = QtWidgets.QSpinBox() self.particleNumberLabel = QtWidgets.QLabel('of xx particles; ') self.spectrumSelector = QtWidgets.QSpinBox() self.spectrumNumberLabel = QtWidgets.QLabel('of xx spectra') for spinbox in [self.particleSelector, self.spectrumSelector]: spinbox.setMinimum(1) spinbox.setSingleStep(1) spinbox.setValue(1) self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector) self.particleSelector.valueChanged.connect(self.setParticleSelector) self.spectrumSelector.valueChanged.connect(self.setSpecSelector) self.layout.addWidget(self.jumpToSpecBtn) self.layout.addWidget(self.specNumberSelector) self.layout.addWidget(QtWidgets.QLabel('Select Polymer Type:')) self.layout.addWidget(self.typeSelectorCombo) self.layout.addStretch() self.layout.addWidget(QtWidgets.QLabel('Select Particle')) self.layout.addWidget(self.particleSelector) self.layout.addWidget(self.particleNumberLabel) self.layout.addStretch() self.layout.addWidget(QtWidgets.QLabel('Select Spectrum')) self.layout.addWidget(self.spectrumSelector) self.layout.addWidget(self.spectrumNumberLabel) def keyPressEvent(self, event): """ If the enter key is pressed, all views are updated to the indicated spectrum number """ if event.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter]: self.JumpToIndicatedSpec.emit() def updateWidgets(self): """ Whenever the particle assignments in the particleContainer have changed, the widges have to be updated. :return: """ uniquePolymers = self.particleContainer.getUniquePolymers() if len(uniquePolymers) > 0: self.lastSpecIndex = self.currentSpectrumIndex self.lastParticleIndex = self.currentParticleIndex self.setDisabled(False) self.typeSelectorCombo.currentIndexChanged.disconnect() self.typeSelectorCombo.clear() self.typeSelectorCombo.addItems(uniquePolymers) self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector) numSpectra = self.particleContainer.getNumberOfMeasurements() self.specNumberSelector.setMaximum(numSpectra) self.setTypeSelector() else: self.setDisabled(True) @QtCore.pyqtSlot(int) def setWidgetsToNewParticleIndex(self, particleIndex): """ When a particle is selected in the sampleview, this method makes updating all widgets accordingly. :return: """ try: self.particleSelector.valueChanged.disconnect() self.spectrumSelector.valueChanged.disconnect() self.typeSelectorCombo.currentIndexChanged.disconnect() except TypeError: pass #signals were not connected, nothing to disconnect... assignment = self.particleContainer.getParticleAssignmentByIndex(particleIndex) self.typeSelectorCombo.setCurrentText(assignment) self.currentParticleIndex = particleIndex partIndices = self.particleContainer.getIndicesOfParticleType(assignment) self.particleSelector.setMaximum(len(partIndices)) self.particleNumberLabel.setText(f'of {len(partIndices)} particles; ') self.particleSelector.setValue(partIndices.index(particleIndex)+1) specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex) self.spectrumSelector.setValue(1) self.spectrumSelector.setMaximum(len(specIndices)) self.spectrumNumberLabel.setText(f'of {len(specIndices)} spectra') self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] self.specNumberSelector.setValue(self.currentSpectrumIndex+1) self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector) self.spectrumSelector.valueChanged.connect(self.setSpecSelector) self.particleSelector.valueChanged.connect(self.setParticleSelector) self.WidgetsUpdated.emit() def setTypeSelector(self): assignment = self.typeSelectorCombo.currentText() partIndices = self.particleContainer.getIndicesOfParticleType(assignment) self.particleSelector.setMaximum(len(partIndices)) self.particleNumberLabel.setText(f'of {len(partIndices)} particles; ') self.particleSelector.setValue(1) self.setParticleSelector() def setParticleSelector(self): assignment = self.typeSelectorCombo.currentText() if assignment != '': # if self.lastParticleIndex is not None: # self.currentParticleIndex = self.lastParticleIndex # self.lastParticleIndex = None # else: partIndices = self.particleContainer.getIndicesOfParticleType(assignment) self.currentParticleIndex = partIndices[self.particleSelector.value()-1] specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex) self.spectrumSelector.setValue(1) self.spectrumSelector.setMaximum(len(specIndices)) self.spectrumNumberLabel.setText(f'of {len(specIndices)} spectra') self.setSpecSelector() def setSpecSelector(self): # if self.lastSpecIndex is not None: # self.currentSpectrumIndex = self.lastSpecIndex # self.lastSpecIndex = None # else: specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex) self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1] self.specNumberSelector.setValue(self.currentSpectrumIndex+1) self.WidgetsUpdated.emit() self.JumpToIndicatedSpec.emit() if self.lastSpecIndex is not None: self.currentParticleIndex = self.particleContainer.getParticleIndexContainingSpecIndex(self.lastSpecIndex) self.lastSpecIndex = None self.setWidgetsToNewParticleIndex(self.currentParticleIndex) class FindColoredParticleWindow(QtWidgets.QWidget): ParticleOfIndexSelected = QtCore.pyqtSignal(int) def __init__(self, analysisparent): super(FindColoredParticleWindow, self).__init__() self.analysisparent = analysisparent self.particleContainer = analysisparent.particleContainer self.coloredParticles = None self.numColoredParticles = None self.currentParticleIndex = None self.label = QtWidgets.QLabel() self.prevBtn = QtWidgets.QPushButton("Go to partilcle 0", self) self.prevBtn.released.connect(self.goToPreviousParticle) self.nextBtn = QtWidgets.QPushButton("Go to particle 1", self) self.nextBtn.released.connect(self.goToNextParticle) self.cancelBtn = QtWidgets.QPushButton("Cancel", self) self.cancelBtn.released.connect(self.close) self.createLayout() self.getColoredParticles() self.goToFirstParticle() def createLayout(self): layout = QtWidgets.QVBoxLayout() self.setLayout(layout) layout.addWidget(self.label) layout.addWidget(self.prevBtn) layout.addWidget(self.nextBtn) layout.addWidget(self.cancelBtn) self.show() def getColoredParticles(self): """ Retrieves a list of Particles from the PrticleContainer with all particles that are not unknown and not white :return: """ self.coloredParticles = self.particleContainer.getIndicesOfColoredNotUnknownParticles() self.numColoredParticles = len(self.coloredParticles) def goToFirstParticle(self): self.currentParticleIndex = 0 self.enableDisableButtons() self.updateToCurrentIndex() def goToNextParticle(self): if self.currentParticleIndex < self.numColoredParticles-1: self.currentParticleIndex += 1 self.updateToCurrentIndex() self.enableDisableButtons() def goToPreviousParticle(self): if self.currentParticleIndex > 0: self.currentParticleIndex -= 1 self.updateToCurrentIndex() self.enableDisableButtons() def enableDisableButtons(self): """ Checks the previous and next particle buttons if they have to be enabled or disabled :return: """ self.prevBtn.setEnabled(True) self.nextBtn.setEnabled(True) if self.currentParticleIndex == 0: self.prevBtn.setDisabled(True) self.prevBtn.setText('No previous particle.') if self.currentParticleIndex == self.numColoredParticles-1: self.nextBtn.setDisabled(True) self.nextBtn.setText('No next particle.') def updateToCurrentIndex(self): """ Updates analysis- and sampleview to display the currently selected particle. :return: """ self.label.setText(f'Displaying Particle {self.currentParticleIndex+1} 0f {self.numColoredParticles}') self.prevBtn.setText(f'Go to revious Particle') self.nextBtn.setText(f'Go to next Particle') indexOfParticleToDisplay = self.coloredParticles[self.currentParticleIndex] self.ParticleOfIndexSelected.emit(indexOfParticleToDisplay) self.analysisparent.jumpToIndicatedSpectrum()