...
 
Commits (21)
......@@ -21,7 +21,7 @@ Requirements:
for 64bit as many use cases require a lot of memory (16 GB better 32 GB
recommended)
* the tsp module in externalmodules can be built with
* the tsp module in external can be built with
python setuptsp.py
please note: for this step a valid compiler needs to be installed in the
system; Otherwise use the precompiled tsp-module
......
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
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 <https://www.gnu.org/licenses/>.
"""
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
import sys
import random
import colorsys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from .analysiswidgets import ExpExcelDialog, AdditiveViewer, ParticleTypeView
from .loadresults import LoadWITecResults
from .particleeditor import ParticleEditor
from .database import DataBaseWindow
from .datastats import DataStats
try:
from .sqlexport import SQLExport
sqlEnabled = True
except:
sqlEnabled = False
class ParticleAnalysis(QtWidgets.QMainWindow):
def __init__(self, dataset, parent=None):
super(ParticleAnalysis, self).__init__(parent)
self.setGeometry(100, 100, 1680, 1050)
self.setWindowTitle('Results of polymer analysis')
self.layout = QtWidgets.QHBoxLayout()
self.widget = QtWidgets.QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.parent = parent
self.datastats = DataStats(dataset)
self.editor = ParticleEditor(self.datastats, self)
self.additivePlot = None
self.importWindow = None
self.polymerCheckBoxes = []
self.lastSelectedCheckBoxNames = []
self.currentParticleIndex = 0
self.currentSpectrumIndex = 0
self.lastSpectrumInFocus = None
self.typeHistogram = ParticleTypeView(self)
self.typeHistogram.indexClicked.connect(self.getAdditivePlot)
self.sizeHistogramCanvas = FigureCanvas(Figure())
sizeHistGroup = QtWidgets.QGroupBox()
sizeHistLayout = QtWidgets.QHBoxLayout()
self.sizeHist_ax = self.sizeHistogramCanvas.figure.subplots()
self.sizeHist_ax.axis('off')
self.sizeHistogramCanvas.figure.subplots_adjust(left=0.1, top=0.93, bottom=0.15, right=0.995)
histNavigation = NavigationToolbar(self.sizeHistogramCanvas, self)
histNavigation.setOrientation(QtCore.Qt.Vertical)
histNavigation.setFixedWidth(50)
sizeHistLayout.addWidget(histNavigation)
sizeHistLayout.addWidget(self.sizeHistogramCanvas)
sizeHistGroup.setLayout(sizeHistLayout)
specGroup = QtWidgets.QGroupBox()
specLayout = QtWidgets.QHBoxLayout()
self.specCanvas = FigureCanvas(Figure())
self.spec_ax = self.specCanvas.figure.subplots()
self.spec_ax.axis("off")
self.ref_ax = self.spec_ax.twinx()
self.specCanvas.figure.subplots_adjust(left=0.1, top=0.93, bottom=0.15, right=0.9)
specNavigation = NavigationToolbar(self.specCanvas, self)
specNavigation.setOrientation(QtCore.Qt.Vertical)
specNavigation.setFixedWidth(50)
specLayout.addWidget(specNavigation)
specLayout.addWidget(self.specCanvas)
specGroup.setLayout(specLayout)
viewLayout = QtWidgets.QVBoxLayout()
self.menuLayout = QtWidgets.QVBoxLayout()
splitter1 = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter1.addWidget(specGroup)
splitter1.addWidget(sizeHistGroup)
splitter2 = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter2.addWidget(splitter1)
splitter2.addWidget(self.typeHistogram)
splitter2.setSizes([300, 150])
self.navigationGroup = QtWidgets.QGroupBox('Navigate through polymers')
self.navigationGroup.setDisabled(True)
navigationLayout = QtWidgets.QHBoxLayout()
self.polymerComboBox = QtWidgets.QComboBox()
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.polymerComboBox.setMinimumWidth(150)
self.particleSelector = QtWidgets.QSpinBox()
self.particleSelector.valueChanged.connect(self.selectParticle)
self.spectrumSelector = QtWidgets.QSpinBox()
self.spectrumSelector.valueChanged.connect(self.selectSpectrum)
for spinbox in [self.particleSelector, self.spectrumSelector]:
spinbox.setMinimum(1)
spinbox.setSingleStep(1)
spinbox.setValue(1)
navigationLayout.addWidget(QtWidgets.QLabel('Select Polymer Type:'))
navigationLayout.addWidget(self.polymerComboBox)
navigationLayout.addStretch()
navigationLayout.addWidget(QtWidgets.QLabel('Select Particle'))
navigationLayout.addWidget(self.particleSelector)
navigationLayout.addWidget(QtWidgets.QLabel('Select Spectrum'))
navigationLayout.addWidget(self.spectrumSelector)
navigationLayout.addStretch()
self.navigationGroup.setLayout(navigationLayout)
referenceGroup = QtWidgets.QGroupBox('Reference Spectra')
referenceLayout = QtWidgets.QHBoxLayout()
self.refSelector = QtWidgets.QComboBox()
self.refSelector.setDisabled(True)
self.dbWin = DataBaseWindow(self)
self.dbWin.selectDataBase(refreshParent=True) #this includes updating the refSelector
self.refSelector.currentIndexChanged.connect(self.updateSpecPlot)
referenceLayout.addWidget(QtWidgets.QLabel('Select Reference'))
referenceLayout.addWidget(self.refSelector)
referenceGroup.setLayout(referenceLayout)
topLayout = QtWidgets.QHBoxLayout()
topLayout.addWidget(self.navigationGroup)
topLayout.addWidget(referenceGroup)
topLayout.addStretch()
viewLayout.addLayout(topLayout)
viewLayout.addWidget(splitter2)
viewLayout.setStretch(1, 1)
self.optionsGroup = QtWidgets.QGroupBox('Further Options')
optionsLayout = QtWidgets.QFormLayout()
self.hqiSpinBox = QtWidgets.QDoubleSpinBox()
self.hqiSpinBox.setValue(75.0)
self.hqiSpinBox.setDecimals(1)
self.hqiSpinBox.setMinimum(0)
optionsLayout.addRow(QtWidgets.QLabel('min HQI:'), self.hqiSpinBox)
self.compHqiSpinBox = QtWidgets.QDoubleSpinBox()
self.compHqiSpinBox.setValue(30.0)
self.compHqiSpinBox.setDecimals(1)
self.compHqiSpinBox.setMinimum(0)
self.compHqiSpinBox.setDisabled(True)
optionsLayout.addRow(QtWidgets.QLabel('min component HQI'), self.compHqiSpinBox)
self.dispResultSpinBox = QtWidgets.QSpinBox()
self.dispResultSpinBox.setValue(20)
self.dispResultSpinBox.setMinimum(1)
self.dispResultSpinBox.valueChanged.connect(self.updateHistogram)
optionsLayout.addRow(QtWidgets.QLabel('Max. items in display:'), self.dispResultSpinBox)
for spinbox in [self.hqiSpinBox, self.compHqiSpinBox]:
spinbox.setMaximum(100)
spinbox.setMaximumWidth(45)
self.updateBtn = QtWidgets.QPushButton('Update Results')
self.updateBtn.setDisabled(True)
optionsLayout.addRow(self.updateBtn)
self.optionsGroup.setLayout(optionsLayout)
self.optionsGroup.setMinimumWidth(175)
self.optionsGroup.setDisabled(True)
self.resultScrollarea = QtWidgets.QScrollArea(self)
self.resultScrollarea.setFixedWidth(250)
self.resultScrollarea.setWidgetResizable(True)
widget = QtWidgets.QWidget()
self.resultScrollarea.setWidget(widget)
self.layout_SArea = QtWidgets.QVBoxLayout(widget)
self.resultCheckBoxes = QtWidgets.QGroupBox('Display Polymer Types:')
self.resultCheckBoxesLayout = QtWidgets.QVBoxLayout()
self.showTotalSelector = QtWidgets.QCheckBox('Show Total Distribution')
self.showTotalSelector.setChecked(True)
self.showTotalSelector.setDisabled(True)
self.resultCheckBoxesLayout.addWidget(self.showTotalSelector)
self.resultCheckBoxesLayout.addStretch()
self.resultCheckBoxes.setLayout(self.resultCheckBoxesLayout)
self.layout_SArea.addWidget(self.resultCheckBoxes)
# self.layout_SArea.addStretch(1)
# self.menuLayout.addWidget(reloadGroup)
self.menuLayout.addWidget(self.optionsGroup)
self.menuLayout.addWidget(self.resultScrollarea)
self.layout.addLayout(self.menuLayout)
self.layout.addLayout(viewLayout)
minHQI = self.datastats.dataset.resultParams['minHQI']
compHQI = self.datastats.dataset.resultParams['compHQI']
if minHQI is not None:
self.hqiSpinBox.setValue(minHQI)
self.compHqiSpinBox.setValue(compHQI)
self.createActions()
self.createMenus()
self.updateData()
def createActions(self):
self.loadTrueMatchAct = QtWidgets.QAction("Load &TrueMatch Results", self)
self.loadTrueMatchAct.triggered.connect(self.importTrueMatchResults)
self.loadTextFileAct = QtWidgets.QAction("Load &ordered Text File", self)
self.loadTextFileAct.setDisabled(True)
self.noOverlayAct = QtWidgets.QAction("&No Overlay", self)
self.selOverlayAct = QtWidgets.QAction("&Selected Overlay", self)
self.fullOverlayAct = QtWidgets.QAction("&Full Overlay", self)
self.transpAct = QtWidgets.QAction("&Transparent Overlay")
self.transpAct.triggered.connect(self.createPolymerOverlay)
self.hideLabelAct = QtWidgets.QAction('&Hide Polymer Numbers', self)
self.hideLabelAct.triggered.connect(self.show_hide_labels)
self.darkenAct = QtWidgets.QAction("&Darken Image", self)
self.darkenAct.triggered.connect(self.darkenBackground)
for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct, self.hideLabelAct, self.transpAct, self.darkenAct]:
act.setCheckable(True)
self.fullOverlayAct.setChecked(True)
self.seedAct = QtWidgets.QAction("&Set Color Seed", self)
self.seedAct.triggered.connect(self.updateColorSeed)
self.databaseAct = QtWidgets.QAction("&ManageDatabase", self)
self.databaseAct.triggered.connect(self.launchDBManager)
self.expExcelAct= QtWidgets.QAction("Export &Excel List", self)
self.expExcelAct.setDisabled(True)
self.expExcelAct.triggered.connect(self.exportToExcel)
self.expSQLAct = QtWidgets.QAction("Export to &SQL Database", self)
self.expSQLAct.setDisabled(True)
self.expSQLAct.triggered.connect(self.exportToSQL)
def createMenus(self):
self.importMenu = QtWidgets.QMenu("&Import Results")
self.importMenu.addActions([self.loadTrueMatchAct, self.loadTextFileAct])
self.dispMenu = QtWidgets.QMenu("&Display", self)
self.overlayActGroup = QtWidgets.QActionGroup(self.dispMenu)
self.overlayActGroup.setExclusive(True)
self.overlayActGroup.triggered.connect(self.createPolymerOverlay)
self.overlayActGroup.triggered.connect(self.updateHistogram)
for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct]:
self.dispMenu.addAction(act)
self.overlayActGroup.addAction(act)
self.dispMenu.addSeparator()
self.dispMenu.addActions([self.transpAct, self.hideLabelAct, self.darkenAct, self.seedAct])
self.refMenu = QtWidgets.QMenu("&References")
self.refMenu.addAction(self.databaseAct)
self.exportMenu = QtWidgets.QMenu("&Export", self)
self.exportMenu.addAction(self.expExcelAct)
self.exportMenu.addAction(self.expSQLAct)
self.menuBar().addMenu(self.importMenu)
self.menuBar().addMenu(self.dispMenu)
self.menuBar().addMenu(self.refMenu)
self.menuBar().addMenu(self.exportMenu)
def launchDBManager(self):
if self.dbWin.isHidden():
self.dbWin.show()
def populateRefSelector(self):
#delete all present entries:
self.refSelector.clear()
if self.dbWin.activeDatabase is None:
self.refSelector.setDisabled(True)
else:
self.refSelector.addItem('')
self.refSelector.addItems(self.dbWin.activeDatabase.spectraNames)
self.refSelector.setDisabled(False)
def updateData(self):
spectra = self.datastats.update()
if spectra is None:
fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Spectra File', self.datastats.dataset.path, 'text file (*.txt)')[0]
spectra = self.datastats.loadSpectra(fname)
if spectra is None:
QtWidgets.QMessageBox.critical(self, 'ERROR!', 'spectra file could not be opened with np.loadtxt...')
return
self.specCanvas.draw()
self.loadParticleData()
def loadParticleData(self):
#check, if dataset already contains results. Otherwise load them...
if not self.datastats.loadParticleData():
self.show()
answer = QtWidgets.QMessageBox.question(self, 'Warning', 'No (or inconsistent) spectra results found, please run import dialog.\nPress OK to import or cancel to set to empty.', QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
if answer == QtWidgets.QMessageBox.Ok:
self.importTrueMatchResults()
elif answer == QtWidgets.QMessageBox.Cancel:
self.datastats.invalidateSpectra()
self.updateBtn.clicked.connect(self.formatResults)
self.formatResults()
else:
self.updateBtn.clicked.connect(self.formatResults)
self.formatResults()
def importTrueMatchResults(self):
self.importWindow = LoadWITecResults(self.datastats, self)
self.importWindow.exec()
@QtCore.pyqtSlot(int)
def getAdditivePlot(self, clickedindex):
polymer = self.datastats.typehistogram[clickedindex][0] #get the polymer name, that was clicked on
if self.datastats.sorted_additives is not None and \
len(self.datastats.sorted_additives[clickedindex]) > 0:
self.additivePlot = AdditiveViewer(polymer, self.datastats.sorted_additives[clickedindex])
self.additivePlot.show()
@QtCore.pyqtSlot()
def formatResults(self):
if self.datastats.spectraResults is not None:
print('formatResults')
self.updateBtn.setDisabled(False)
self.optionsGroup.setDisabled(False)
self.datastats.formatResults(self.hqiSpinBox.value(), self.compHqiSpinBox.value())
if self.datastats.additiveResults is not None:
self.compHqiSpinBox.setDisabled(False)
self.createHistogramData()
def createHistogramData(self):
if not self.datastats.createHistogramData():
QtWidgets.QMessageBox.critical(self, 'Error', 'Inconsistent particle data. Please restore backup!')
return
###Handle Checkboxes for all polymers...
self.menuLayout.removeWidget(self.resultScrollarea)
for i in [self.resultCheckBoxes, self.resultCheckBoxesLayout, self.resultScrollarea, self.layout_SArea]:
i.setParent(None)
del i
for i in self.polymerCheckBoxes: #remove present boxlabels
i.setParent(None)
del i
self.showTotalSelector.setParent(None)
self.showTotalSelector.setDisabled(False)
self.showTotalSelector.stateChanged.connect(self.updateHistogram)
del self.resultCheckBoxes
del self.resultCheckBoxesLayout
del self.resultScrollarea
del self.layout_SArea
self.resultScrollarea = QtWidgets.QScrollArea(self)
self.resultScrollarea.setFixedWidth(250)
self.resultScrollarea.setWidgetResizable(True)
widget = QtWidgets.QWidget()
self.resultScrollarea.setWidget(widget)
self.layout_SArea = QtWidgets.QVBoxLayout(widget)
self.resultCheckBoxes = QtWidgets.QGroupBox('Show Polymer Types:')
self.resultCheckBoxesLayout = QtWidgets.QVBoxLayout()
self.resultCheckBoxesLayout.addWidget(self.showTotalSelector)
#generate new checkboxes
self.polymerCheckBoxes = []
uniquePolymers = self.datastats.getUniquePolymers()
for index, polymer in enumerate(uniquePolymers):
self.polymerCheckBoxes.append(QtWidgets.QCheckBox(self))
self.polymerCheckBoxes[index].setText(polymer)
self.resultCheckBoxesLayout.addWidget(self.polymerCheckBoxes[index])
if polymer in self.lastSelectedCheckBoxNames:
self.polymerCheckBoxes[index].setChecked(True)
self.polymerCheckBoxes[index].stateChanged.connect(self.updateHistogram)
self.polymerCheckBoxes[index].stateChanged.connect(self.createPolymerOverlay)
self.resultCheckBoxesLayout.addStretch()
self.resultCheckBoxes.setLayout(self.resultCheckBoxesLayout)
self.layout_SArea.addWidget(self.resultCheckBoxes)
self.menuLayout.addWidget(self.resultScrollarea)
self.expExcelAct.setDisabled(False)
if sqlEnabled:
self.expSQLAct.setDisabled(False)
self.navigationGroup.setEnabled(True)
self.polymerComboBox.currentIndexChanged.disconnect()
self.polymerComboBox.clear()
self.polymerComboBox.addItems(uniquePolymers)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.polymerIndex = self.polymerComboBox.currentIndex()
if self.lastSpectrumInFocus is not None:
self.currentSpectrumIndex = self.lastSpectrumInFocus
self.displayNewPolymerType(resetCurrentIndex=False)
else:
self.displayNewPolymerType()
self.updateHistogram()
self.createPolymerOverlay()
def exportToExcel(self):
expWin = ExpExcelDialog(self.datastats, self)
expWin.exec()
def exportToSQL(self):
sqlexp = SQLExport(self.datastats, self)
sqlexp.exec()
def updateSpecPlot(self, centerOn=True, highlightContour=True):
#draw Sample Spectrum
specIndex = self.currentSpectrumIndex
spectra = self.datastats.spectra
particlestats = self.datastats.getParticleStats()
self.spec_ax.axis("on")
self.spec_ax.clear()
self.spec_ax.plot(spectra[:, 0], spectra[:, specIndex+1])
self.spec_ax.tick_params(axis='both', which='both', labelsize=15)
self.spec_ax.set_xlabel('Wavenumber (cm-1)', fontsize = 15)
self.spec_ax.set_ylabel('Counts', fontsize = 15)
self.spec_ax.set_title('ScanPoint Number {}, Size = {} µm'.format(specIndex+1,
np.round(particlestats[self.currentParticleIndex][2], 1)))
self.spec_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
wavenumber_diff = list(spectra[:, 0]-100)
y_start = wavenumber_diff.index(min(wavenumber_diff))
y_min = min(spectra[y_start:, specIndex+1])
y_max = max(spectra[y_start:, specIndex+1])
self.spec_ax.set_ybound(0.9*y_min, 1.1*y_max)
#draw Reference
self.ref_ax.clear()
if self.refSelector.isEnabled() and self.refSelector.currentText() != '':
self.ref_ax.tick_params(axis='both', which='both', labelsize=15)
refID = self.dbWin.activeDatabase.spectraNames.index(self.refSelector.currentText())
ref = self.dbWin.activeDatabase.spectra[refID]
self.ref_ax.plot(ref[:, 0], ref[:, 1], color = 'r')
self.ref_ax.set_ylabel('Ref. Intensity', fontsize = 15, color = 'r')
self.ref_ax.tick_params('y', colors = 'r')
self.ref_ax.set_xbound(100, (3400 if spectra[-1, 0] > 3400 else spectra[-1, 0]))
# wavenumber_diff = list(ref[:, 0]-100)
# y_start = wavenumber_diff.index(min(wavenumber_diff))
# y_min = min(ref[y_start:, specIndex+1])
# y_max = max(ref[y_start:, specIndex+1])
self.spec_ax.figure.canvas.draw()
self.parent.centerOnRamanIndex(specIndex, centerOn=centerOn, highlightContour=highlightContour)
self.parent.highLightRamanIndex(specIndex)
self.lastSpectrumInFocus = specIndex
def selectContour(self, index, centerOn=True):
uniquePolymers = self.datastats.getUniquePolymers()
if uniquePolymers is not None:
#the index is the contour index, find particle index:
specIndex = self.datastats.particles2spectra[index][0] #select first spectrum of partoicle
self.currentParticleIndex = index
self.currentSpectrumIndex = specIndex
selectedPolymer = self.datastats.currentPolymers[specIndex]
try:
self.polymerIndex = uniquePolymers.index(selectedPolymer)
except:
print(selectedPolymer)
raise
#subparticleIndex
partIndicesOfThatPolymer = self.datastats.indices[self.polymerIndex]
subPartInd = partIndicesOfThatPolymer.index(index)
#disconnect analysis widgets:
self.particleSelector.valueChanged.disconnect()
self.spectrumSelector.valueChanged.disconnect()
self.polymerComboBox.currentIndexChanged.disconnect()
#set widgets...
self.particleSelector.setValue(subPartInd+1)
self.particleSelector.setMaximum(len(partIndicesOfThatPolymer))
self.spectrumSelector.setValue(1)
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[index]))
selectedPolymer = self.datastats.currentPolymers[specIndex]
self.polymerIndex = uniquePolymers.index(selectedPolymer)
self.polymerComboBox.setCurrentIndex(self.polymerIndex)
#reconnect all widgets:
self.particleSelector.valueChanged.connect(self.selectParticle)
self.spectrumSelector.valueChanged.connect(self.selectSpectrum)
self.polymerComboBox.currentIndexChanged.connect(self.displayNewPolymerType)
self.updateSpecPlot(centerOn=centerOn)
def displayNewPolymerType(self, resetCurrentIndex=True):
self.polymerIndex = self.polymerComboBox.currentIndex()
self.particleSelector.setMaximum(len(self.datastats.indices[self.polymerIndex]))
if resetCurrentIndex:
self.particleSelector.setValue(1)
self.spectrumSelector.setValue(1)
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[self.currentParticleIndex]))
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot(centerOn=True)
else:
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot(centerOn=False)
def selectParticle(self, resetSpectrumCount=True):
if self.datastats.particles2spectra is not None:
self.currentParticleIndex = self.datastats.indices[self.polymerIndex][self.particleSelector.value()-1]
self.spectrumSelector.setMaximum(len(self.datastats.particles2spectra[self.currentParticleIndex]))
if resetSpectrumCount:
self.spectrumSelector.setValue(1)
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot()
else:
print('no spectrum assignment found...')
def selectSpectrum(self):
if self.datastats.particles2spectra is not None:
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot()
def updateHistogram(self):
#draw the general histogram
colorList = []
abundancyList = []
labelList = []
for index, i in enumerate(self.datastats.typehistogram):
if not self.selOverlayAct.isChecked() or self.polymerCheckBoxes[index].isChecked():
abundancyList.append(self.datastats.typehistogram[index][1])
curColor = self.getColorFromName(self.datastats.typehistogram[index][0])
colorList.append(QtGui.QColor(*curColor))
if self.datastats.sorted_additives is None:
numads = ''
else:
numads = len(np.unique(self.datastats.sorted_additives[index]))
if numads == 0:
numads = ''
else:
numads = '(' + str(numads) + ')'
numpolymers = i[1]
label = ('{} x ' + self.datastats.typehistogram[index][0] + ' {}').format(numpolymers, numads)
labelList.append(label)
print("abundancyList:", abundancyList)
print("labelList:", labelList)
print("colorList:", colorList)
self.typeHistogram.updateTypes(list(zip(abundancyList, labelList, colorList)))
#general size histogram
self.sizeHist_ax.clear()
self.sizeHist_ax.axis('on')
self.bins = np.logspace(0.1, 3, 20)
self.sizes = [i[0] if np.isnan(i[2]) else i[2] for i in self.datastats.getParticleStats()] #extract long size (if ellipse fit is nan -> box fit)
sizehist = np.histogram(self.sizes, self.bins)
self.totalhistx = []
for i in range(19):
self.totalhistx.append(np.mean((sizehist[1][i], sizehist[1][i+1])))
self.totalhisty = sizehist[0]
self.sizeHist_ax.tick_params(axis='both', which='both', labelsize=15)
self.sizeHist_ax.set_xlabel('Size (µm)', fontsize = 15)
self.sizeHist_ax.set_ylabel('Number', fontsize = 15)
self.sizeHist_ax.set_xlim(3, 1100)
self.sizeHist_ax.figure.canvas.draw()
if self.showTotalSelector.isChecked():
self.sizeHist_ax.semilogx(self.totalhistx, self.totalhisty, label = 'total')
#get selected boxes
selected = []
for i in self.polymerCheckBoxes:
if i.isChecked() == True:
selected.append(i.text())
for i in selected:
sizes = [self.sizes[index] for index in range(len(self.sizes))
if self.datastats.currentPolymers[index] == i]
sizehist = np.histogram(sizes, self.bins)
self.sizeHist_ax.semilogx(self.totalhistx, sizehist[0], label = i, color = self.getColorFromName(i, base255 = False))
self.sizeHist_ax.legend(prop = {'size': 15})
self.sizeHist_ax.tick_params(axis='both', which='both', labelsize=15)
self.sizeHist_ax.set_xlabel('Size (µm)', fontsize = 15)
self.sizeHist_ax.set_ylabel('Number', fontsize = 15)
self.sizeHist_ax.set_xlim(3, 1100)
self.sizeHist_ax.figure.canvas.draw()
self.lastSelectedCheckBoxNames = [checkbox.text() for checkbox in self.polymerCheckBoxes if checkbox.isChecked()]
def darkenBackground(self):
self.parent.darkenPixmap = self.darkenAct.isChecked()
if self.darkenAct.isChecked():
self.parent.scene().setBackgroundBrush(QtGui.QColor(5, 5, 5))
self.parent.item.setOpacity(0.2)
else:
self.parent.scene().setBackgroundBrush(QtCore.Qt.darkGray)
self.parent.item.setOpacity(1)
def updateColorSeed(self):
text, ok = QtWidgets.QInputDialog.getText(self, 'Color Seed', 'Enter New Seed here', text=self.datastats.colorSeed)
if ok:
self.datastats.colorSeed = text
self.datastats.dataset.colorSeed = text
self.updateHistogram()
self.createPolymerOverlay()
def getColorFromName(self, name, base255=True):
random.seed(self.datastats.colorSeed + name)
hue = random.random()
random.seed((self.datastats.colorSeed + name)*2)
saturation = random.random()/4 + 0.75 #i.e., between 0.75 and 1
random.seed((self.datastats.colorSeed + name)*3)
value = random.random()/5 + 0.8 #i.e., between 0.8 and 1
color = colorsys.hsv_to_rgb(hue, saturation, value)
if base255:
color = list(color)
for i in range(3):
color[i] = np.round(color[i]*255)
color = tuple(color)
return color
def createPolymerOverlay(self):
uniquePolymers = self.datastats.getUniquePolymers()
if not self.noOverlayAct.isChecked() and self.datastats.indices is not None:
if len(self.datastats.indices) > 0:
alpha = (128 if self.transpAct.isChecked() else 255)
#get colors for each polymer type
colorList = [QtGui.QColor(255, 255, 255, alpha=50)]*len(self.datastats.particleResults)
legendItems = []
for index, indexList in enumerate(self.datastats.indices):
if self.fullOverlayAct.isChecked() or (self.selOverlayAct.isChecked() and self.polymerCheckBoxes[index].isChecked()):
color = self.getColorFromName(uniquePolymers[index], base255=True)
color = QtGui.QColor(color[0], color[1], color[2], alpha=alpha)
legendItems.append((uniquePolymers[index], color))
for i in indexList:
colorList[i] = color
self.parent.contouritem.colorList = colorList
self.parent.contouritem.update()
self.parent.imparent.legend.setTextColorItems(legendItems)
self.parent.imparent.legend.show()
else:
self.parent.contouritem.colorList = []
self.parent.contouritem.update()
self.parent.imparent.legend.setTextColorItems([])
self.parent.imparent.legend.hide()
def show_hide_labels(self):
hidden = self.hideLabelAct.isChecked()
for scanIndicator in self.parent.ramanscanitems:
scanIndicator.hidden = hidden
scanIndicator.update()
def closeEvent(self, event):
for window in [self.additivePlot, self.importWindow, self.dbWin]:
try: window.close()
except: pass
self.parent.imparent.particelAnalysisAct.setChecked(False)
event.accept()
if __name__ == '__main__':
from ..dataset import DataSet
def run():
app = QtWidgets.QApplication(sys.argv)
meas = ParticleAnalysis(DataSet("dummydata"))
meas.showMaximized()
return app.exec_()
run()
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
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 <https://www.gnu.org/licenses/>.
"""
from PyQt5 import QtWidgets, QtGui, QtCore
import numpy as np
import pandas as pd
import os
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class ExpExcelDialog(QtWidgets.QDialog):
def __init__(self, datastats, parent):
super(ExpExcelDialog, self).__init__(parent)
self.setWindowTitle('Export Options')
self.setGeometry(200, 200, 300, 300)
self.datastats = datastats
self.particles = self.datastats.getParticleStats()
self.polymers = self.datastats.particleResults
self.additives = self.datastats.currentAdditives
self.hqis = self.datastats.hqis
self.layout = QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
excelvbox = QtWidgets.QVBoxLayout()
excelvbox.addWidget(QtWidgets.QLabel('Select Parameters for Export'))
excelgroup = QtWidgets.QGroupBox("Export to Excel", self)
self.exportOptions = ['Polymer Type (mandatory)', 'Additives', 'Long Size (µm)', 'Short Size (µm)', 'Area (µm²)', 'HQI', 'Size Classes']
self.checkBoxes = []
self.sizeClasses = [5, 10, 20, 50, 100, 1e6]
self.directory = self.datastats.dataset.path
for index, option in enumerate(self.exportOptions):
self.checkBoxes.append(QtWidgets.QCheckBox(self))
self.checkBoxes[-1].setText(option)
self.checkBoxes[-1].setChecked(True)
if option == 'Polymer Type (mandatory)':
self.checkBoxes[-1].setEnabled(False) #is mandatory!!!
if option == 'Additives':
if self.additives is None:
self.checkBoxes[-1].setEnabled(False)
self.checkBoxes[-1].setChecked(False)
excelvbox.addWidget(self.checkBoxes[-1])
self.xlsFileName = QtWidgets.QLineEdit()
self.xlsFileName.setText('{}_Particle_List'.format(self.datastats.dataset.name))
excelvbox.addWidget(QtWidgets.QLabel('Filename:'))
excelvbox.addWidget(self.xlsFileName)
self.exlbtn = QtWidgets.QPushButton('Export to Excel')
self.exlbtn.resize(self.exlbtn.sizeHint())
self.exlbtn.clicked.connect(self.toExcel)
excelvbox.addWidget(self.exlbtn)
excelgroup.setLayout(excelvbox)
self.layout.addWidget(excelgroup)
self.show()
def toExcel(self):
requiredcolumns = []
self.sizes = np.round(np.array([i[0] if np.isnan(i[2]) else i[2] for i in self.particles]), 1)
for box in self.checkBoxes:
if box.isChecked() == True:
if box.text() != 'Size Classes':
requiredcolumns.append(box.text())
if box.text() == 'Long Size (µm)':
longSize = self.sizes
elif box.text() == 'Short Size (µm)':
shortSize = np.round(np.array([i[1] if np.isnan(i[3]) else i[3] for i in self.particles]), 1)
elif box.text() == 'Area (µm²)':
area = np.array([np.round(float(entry[4]), 1) for entry in self.particles])
else:
requiredcolumns.append('0 - 5 µm')
requiredcolumns.append('5 - 10 µm')
requiredcolumns.append('10 - 20 µm')
requiredcolumns.append('20 - 50 µm')
requiredcolumns.append('50 - 100 µm')
requiredcolumns.append('> 100 µm')
finalData = np.zeros((self.polymers.shape[0],len(requiredcolumns)-1))
polymertypes = [""]*self.polymers.shape[0]
rowindex = 0
for polymer in np.unique(self.polymers):
indices = self.polymers == polymer
numentries = int(np.sum(indices))
print("Num:", numentries)
sys.stdout.flush()
for colindex, column in enumerate(requiredcolumns):
if column == 'Polymer Type (mandatory)':
polymertypes[rowindex:rowindex+numentries] = self.polymers[indices]
if column == 'Additives':
finalData[rowindex:rowindex+numentries, colindex-1] = self.additives[indices]
if column == 'Long Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = longSize[indices]
if column == 'Short Size (µm)':
finalData[rowindex:rowindex+numentries, colindex-1] = shortSize[indices]
if column == 'Area (µm²)':
finalData[rowindex:rowindex+numentries, colindex-1] = area[indices]
# hit quality index array does not match the data size if particles have been combined
#if column == 'HQI':
# finalData[rowindex:rowindex+numentries, colindex-1] = self.hqis[indices]
if '> 100 µm' in requiredcolumns:
##append size classes
numPrevCols = len(requiredcolumns) - 1 - len(self.sizeClasses) #number of previous columns
for tableindex, dataindex in enumerate(np.arange(len(indices))[indices]):
for classindex in range(len(self.sizeClasses)):
upLimit = self.sizeClasses[classindex]
if classindex == 0: lowLimit = 0
else: lowLimit = self.sizeClasses[classindex-1]
curSize = self.sizes[dataindex]
if curSize > lowLimit and curSize <= upLimit:
finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(1)
else:
finalData[rowindex+tableindex, numPrevCols + classindex] = np.int(0)
rowindex = rowindex + numentries
#dump into excel file
xlsname = self.directory + '//' + self.xlsFileName.text() + '.xlsx'
print('exporting excel to:\n file name: {} in directory: {}'.format(self.xlsFileName.text(), self.directory))
validFileName = False
incr = 1
while not validFileName:
if not os.path.exists(xlsname):
validFileName = True
else:
xlsname = self.directory + '//' + self.xlsFileName.text() + ' {}.xlsx'.format(incr)
incr += 1
writer = pd.ExcelWriter(xlsname, engine = 'xlsxwriter')
df = pd.DataFrame(finalData, columns=requiredcolumns[1:])
df.insert(0, 'Polymer Type', polymertypes)
df.to_excel(writer, sheet_name = 'Individual Particles', index = False)
if '> 100 µm' in requiredcolumns:
#generate particle statistics report
header = ['0 - 5 µm', '5 - 10 µm', '10 - 20 µm', '20 - 50 µm', '50 - 100 µm', '> 100 µm']
index = np.unique(self.polymers)
particleclasses = []
for polymer in index:
indices = np.where(self.polymers == polymer)[0]
sortind = np.searchsorted([5,10,20,50,100], self.sizes[indices], 'right')
classes = np.bincount(sortind, minlength=6)
particleclasses.append(classes)
particleclasses = np.array(particleclasses)
report = pd.DataFrame(np.array(particleclasses), columns=header,
dtype=int)
report.insert(0, 'Polymer Type', index)
report.insert(len(report.columns), 'Sum total', particleclasses.sum(axis=1))
report.to_excel(writer, sheet_name = 'Particle Statistics', index=False)
writer.save()
self.accept()
class AdditiveViewer(QtWidgets.QWidget):
def __init__(self, polymername, sortedAdditives):
super(AdditiveViewer, self).__init__()
self.setGeometry(200,200, 800, 600)
self.setWindowTitle('Additives of {}'.format(polymername))
self.layout = QtWidgets.QGridLayout()
self.setLayout(self.layout)
self.canvas = FigureCanvas(Figure(figsize=(5, 3)))
self.ax = self.canvas.figure.subplots()
self.layout.addWidget(self.canvas, 0, 0)
self.ax.hist(sortedAdditives)
self.ax.set_ylabel('Number', fontsize = 15)
self.ax.tick_params(axis='both', which='both', labelsize=15)
class ParticleIndicator(QtWidgets.QPushButton):
def __init__(self, number, numtotal, color, text, parent=None):
super().__init__(parent)
self.number = number
self.numtotal = numtotal
self.color = color
self.text = text
self.setFixedHeight(30)
def paintEvent(self, event):
r = self.number/self.numtotal
width = self.width()
height = self.height()
qp = QtGui.QPainter()
qp.begin(self)
#qp.fillRect(self.rect(), QtCore.Qt.white)
qp.setBrush(QtCore.Qt.white)
qp.drawRoundedRect(0, 0, width, height, 5. ,5.)
qp.setPen(self.color)
qp.setBrush(self.color)
qp.drawRoundedRect(0, 0, int(width*r), height, 5. ,5.)
qp.setPen(QtCore.Qt.black)
qp.setBrush(QtCore.Qt.NoBrush)
qp.drawRoundedRect(0, 0, width, height, 5. ,5.)
font = qp.font()
font.setPointSize(13)
font.setStyleStrategy(QtGui.QFont.NoAntialias)
font.setWeight(0)
qp.setFont(font)
qp.setCompositionMode(QtGui.QPainter.RasterOp_SourceXorDestination)
qp.setPen(QtCore.Qt.white)
qp.drawText(5, 0, width-10, height, QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter,
self.text)
qp.end()
class ParticleTypeView(QtWidgets.QScrollArea):
indexClicked = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self.view = QtWidgets.QWidget(self)
self.view.setCursor(QtGui.QCursor(QtCore.Qt.WhatsThisCursor))
self.view.setMinimumWidth(250)
group = QtWidgets.QGroupBox('Polymer Type Distribution', self.view)
self.indicatorbox = QtWidgets.QVBoxLayout()
self.indicatorbox.setContentsMargins(5,5,5,5)
group.setLayout(self.indicatorbox)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(group)
self.view.setLayout(hbox)
self.setWidgetResizable(True)
self.setWidget(self.view)
self.setAlignment(QtCore.Qt.AlignHCenter)
self.widgets = []
def updateTypes(self, types):
print("Updating polymer type view", flush=True)
for pi in self.widgets:
self.indicatorbox.removeWidget(pi)
pi.setParent(None)
pi.destroy()
self.indicatorbox.takeAt(0)
self.widgets = []
numtotal = sum([num for num, text, color in types])
def getIndexFunction(index):
return lambda : self.indexClicked.emit(index)
for index, entry in enumerate(types):
num, text, color = entry
print("num, text, color:", num, text, color, flush=True)
pi = ParticleIndicator(num, numtotal, color, text)
self.indicatorbox.addWidget(pi)
pi.clicked.connect(getIndexFunction(index))
self.widgets.append(pi)
self.indicatorbox.addStretch()
self.view.update()
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
GEPARD - Gepard-Enabled PARticle Detection
Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für
Polymerforschung Dresden e. V. <bittrich-lars@ipfdd.de>
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 <https://www.gnu.org/licenses/>.
"""
"""
The als_baseline method, including the WhittakerSmoother Class was taken from
https://github.com/all-umass/superman/blob/master/superman/baseline/als.py
with permission from github user CJ Carey (perimosocordiae)
"""
from PyQt5 import QtWidgets, QtCore
import numpy as np
import sys
import dill
import os
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import functools
from scipy.linalg import solveh_banded
from pathlib import Path
class DataBaseWindow(QtWidgets.QMainWindow):
def __init__(self, parent):
super(DataBaseWindow, self).__init__()
self.parent = parent
self.path = os.path.join(Path.home(), 'gepard', 'databases')
self.importPath = self.path
if not os.path.exists(self.path):
os.mkdir(self.path)
self.activeDatabase = None
self.activeSpectrum = None
self.activeSpectrumName = None
self.activeSpectrumIndex = None
self.databases = []
self.setWindowTitle('Edit Database')
self.setGeometry(200, 200, 1280, 720)
self.widget = QtWidgets.QWidget()
self.setCentralWidget(self.widget)
self.layout = QtWidgets.QGridLayout()
self.widget.setLayout(self.layout)
groupLayout = QtWidgets.QVBoxLayout()
self.db_selector = QtWidgets.QComboBox()
self.db_selector.currentIndexChanged.connect(self.selectDataBase)
self.databases = self.getDatabases()
self.updateDBSelectorList()
self.saveBtn = QtWidgets.QPushButton('Save databases')
self.saveBtn.clicked.connect(self.save)
groupLayout.addWidget(QtWidgets.QLabel('Select Active Database:'))
groupLayout.addWidget(self.db_selector)
groupLayout.addWidget(self.saveBtn)
groupBox = QtWidgets.QGroupBox()
groupBox.setLayout(groupLayout)
groupBox.setMaximumWidth(250)
self.layout.addWidget(groupBox, 1, 0)
self.modifyGroup = QtWidgets.QGroupBox('Modify Spectra')
modifyLayout = QtWidgets.QVBoxLayout()
self.modifyGroup.setDisabled(True)
self.blCorrectBtn = QtWidgets.QPushButton('Baseline Correct')
self.blCorrectBtn.clicked.connect(self.baselineCorrect)
self.cropBtn = QtWidgets.QPushButton('Crop Spectra')
self.cropBtn.clicked.connect(self.crop)
self.normalizeBtn = QtWidgets.QPushButton('Normalize')
self.normalizeBtn.clicked.connect(self.normalize)
self.resetBtn = QtWidgets.QPushButton('Restore Original Spectra')
self.resetBtn.clicked.connect(self.resetSpectra)
modifyLayout.addWidget(self.cropBtn)
modifyLayout.addWidget(self.blCorrectBtn)
modifyLayout.addWidget(self.normalizeBtn)
modifyLayout.addStretch()
modifyLayout.addWidget(self.resetBtn)
self.modifyGroup.setLayout(modifyLayout)
self.layout.addWidget(self.modifyGroup, 2, 0)
self.createActions()
self.createMenus()
self.updateDBInspector()
if self.parent is None:
self.show()
def createActions(self):
self.newDBAct = QtWidgets.QAction('Create &New Database', self)
self.newDBAct.triggered.connect(self.createNewDB)
self.importWITecAct = QtWidgets.QAction('Import &WITec Spectrum', self)
self.importWITecAct.triggered.connect(self.addWITecSpectra)
self.importReniAct = QtWidgets.QAction('Import &Renishaw Spectrum', self)
self.importReniAct.triggered.connect(self.addReniSpectra)
self.importReniAct.setDisabled(True)
def createMenus(self):
self.toolsMenu = QtWidgets.QMenu("&Tools", self)
self.importMenu = QtWidgets.QMenu("&ImportSpectra", self)
self.importMenu.addAction(self.importWITecAct)
self.importMenu.addAction(self.importReniAct)
self.toolsMenu.addAction(self.newDBAct)
self.toolsMenu.addSeparator()
self.toolsMenu.addMenu(self.importMenu)
self.menuBar().addMenu(self.toolsMenu)
def getDatabases(self):
dbInPath = [file for file in os.listdir(self.path) if file.endswith('.db')]
databases = []
for db in dbInPath:
with open(os.path.join(self.path, db), 'rb') as f:
databases.append(dill.load(f))
return databases
def createNewDB(self):
text, okPressed = QtWidgets.QInputDialog.getText(self, 'Name?', 'Name:', QtWidgets.QLineEdit.Normal, 'NewDatabase')
if okPressed and text != '':
#is there already a db with that name?
db_exists = False
for db in self.databases:
if db.title == text:
db_exists = True
QtWidgets.QMessageBox.about(self, 'Error', 'Database {} already exists.\nPlease select another name.'.format(text))
self.selectDataBase()
if not db_exists:
self.databases.append(Database(text))
self.activeDatabase = self.databases[-1]
self.updateDBSelectorList()
def addWITecSpectra(self):
fnames = QtWidgets.QFileDialog.getOpenFileNames(self, "Select Spectrum File", self.importPath, "*.txt")[0]
if fnames:
self.importPath = os.path.basename(fnames[0])
for fname in fnames:
with open(fname) as file:
specName = fname.split('/')[-1].split('.')[0]
curSpectrum = [line.strip().split('\t') for line in file]
numRows = len(curSpectrum)
numCols = len(curSpectrum[0])
if numRows !=2 and numCols != 2:
QtWidgets.QMessageBox.about(self, 'Error', 'Invalid dimensions for', fname)
if numCols == 2: # normally expected structure
xaxis = [np.float(i[0]) for i in curSpectrum]
yaxis = [np.float(i[1]) for i in curSpectrum]
else: #inversed structure...
xaxis = [np.float(i) for i in curSpectrum[0]]
yaxis = [np.float(i) for i in curSpectrum[1]]
addSpectrum = np.zeros((len(xaxis), 2))
addSpectrum[:, 0] = np.array(xaxis)
addSpectrum[:, 1] = np.array(yaxis)
self.includeSpectrum(addSpectrum, specName)
self.updateDBInspector()
self.parent.populateRefSelector()
def addReniSpectra(self):
pass
def includeSpectrum(self, spec, specName):
self.activeDatabase.spectra.append(spec)
self.activeDatabase.origSpectra.append(spec.copy())
self.activeDatabase.spectraNames.append(specName)
def baselineCorrect(self):
self.blWin = BaseLineCorrect(self.activeDatabase.spectra.copy(), parent = self)
def crop(self):
self.cropWin = CropSpectra(self.activeDatabase.spectra.copy(), parent = self)
def normalize(self):
prompt = QtWidgets.QMessageBox.question(self, 'Subtract minimum?', 'Click YES for additional Subtraction of minimum intensity\nClick NO to keep current minimum (recommended, if baseline subtraction was performed).',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
for i in range(len(self.activeDatabase.spectra)):
curSpec = self.activeDatabase.spectra[i]
if prompt == QtWidgets.QMessageBox.Yes:
curSpec[:, 1] = curSpec[:, 1] - curSpec[:, 1].min()
curSpec[:, 1] = curSpec[:, 1] / curSpec[:, 1].max()
self.activeDatabase.spectra[i] = curSpec
self.databases[self.activeDatabaseIndex] = self.activeDatabase
self.drawSpectrum(self.activeSpectrum, self.activeSpectrumName)
def updateDBInspector(self):
if self.activeDatabase is not None:
try: self.modifyGroup.setDisabled(False)
except: pass
#reset list and layout entries
self.spectraLabels = []
#remove contents from old layout...
try:
oldLayout = self.dbExploreGroup.layout()
for i in reversed(range(oldLayout.count())):
oldLayout.itemAt(i).widget().setParent(None)
except:
pass
self.dbExploreGroup = QtWidgets.QGroupBox()
dbExploreLayout = QtWidgets.QGridLayout()
#spectra list
spectraGroup = QtWidgets.QGroupBox('Spectra in Database')
spectraGroup.setFixedWidth(180)
spectraGroupLayout = QtWidgets.QVBoxLayout()
#add QLabelss
if self.activeDatabase is not None:
for index, spectrum in enumerate(self.activeDatabase.spectraNames):
self.spectraLabels.append(QtWidgets.QLabel(spectrum))
self.spectraLabels[-1].mousePressEvent = functools.partial(self.selectSpectrum, source_object=self.spectraLabels[-1]) #make QLabel clickable
spectraGroupLayout.addWidget(self.spectraLabels[-1])
spectraGroup.setLayout(spectraGroupLayout)
spectraArea = QtWidgets.QScrollArea(self)
spectraArea.setFixedWidth(200)
spectraArea.setWidget(spectraGroup)
dbExploreLayout.addWidget(spectraArea, 1, 0)
self.removeSpecBtn = QtWidgets.QPushButton('Remove Spectrum')
self.removeSpecBtn.setDisabled(True)
self.removeSpecBtn.clicked.connect(self.removeSpectrum)
if self.activeDatabase is None:
self.databaseLabel = QtWidgets.QLabel()
else:
self.databaseLabel = QtWidgets.QLabel('{} ({} spectra)'.format(self.activeDatabase.title, len(self.activeDatabase.spectra)))
dbExploreLayout.addWidget(self.removeSpecBtn, 0, 0)
dbExploreLayout.addWidget(self.databaseLabel, 0, 1)
#plot:
self.spectraPlot = FigureCanvas(Figure(figsize=(5, 3)))
self.spectra_ax = self.spectraPlot.figure.subplots()
self.spectra_ax.axis('off')
self.spectra_toolbar = NavigationToolbar(self.spectraPlot, self)
self.spectra_toolbar.setOrientation(QtCore.Qt.Vertical)
self.spectra_toolbar.setFixedWidth(50)
dbExploreLayout.addWidget(self.spectra_toolbar, 1, 1)
dbExploreLayout.addWidget(self.spectraPlot, 1, 2)
self.dbExploreGroup.setLayout(dbExploreLayout)
self.layout.addWidget(self.dbExploreGroup, 1, 1, 2, 4)
if self.activeDatabase is not None and len(self.activeDatabase.spectra) > 0 and self.activeSpectrumIndex is None:
self.activeSpectrum = self.activeDatabase.spectra[0]
self.activeSpectrumName = self.activeDatabase.spectraNames[0]
self.activeSpectrumIndex = 0
if self.activeSpectrum is not None:
self.drawSpectrum(self.activeSpectrum, self.activeSpectrumName)
def selectSpectrum(self, event, source_object=None):
self.activeSpectrumIndex = self.activeDatabase.spectraNames.index(source_object.text())
self.activeSpectrum = self.activeDatabase.spectra[self.activeSpectrumIndex]
self.activeSpectrumName = source_object.text()
self.drawSpectrum(self.activeSpectrum, self.activeSpectrumName)
def removeSpectrum(self):
buttonPrompt = QtWidgets.QMessageBox.question(self, 'Confirm spectrum removal', 'Really remove {}?'.format(self.activeSpectrumName),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if buttonPrompt == QtWidgets.QMessageBox.Yes:
specIndex = self.activeDatabase.spectraNames.index(self.activeSpectrumName)
del self.activeDatabase.spectra[specIndex]
del self.activeDatabase.spectraNames[specIndex]
del self.activeDatabase.origSpectra[specIndex]
self.updateDBInspector()
def resetSpectra(self):
if self.activeDatabase is not None:
answer = QtWidgets.QMessageBox.question(self, 'Warning', 'Restore Original Spectra?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
self.activeDatabase.spectra = self.activeDatabase.origSpectra
self.databases[self.activeDatabaseIndex].spectra = self.activeDatabase.origSpectra
self.activeDatabase = self.databases[self.activeDatabaseIndex]
self.activeSpectrum = self.activeDatabase.spectra[self.activeSpectrumIndex]
self.drawSpectrum(self.activeSpectrum, self.activeSpectrumName)
def drawSpectrum(self, spectrum, name):
self.spectra_ax.clear()
self.spectra_ax.axis('on')
self.spectra_ax.plot(spectrum[:, 0], spectrum[:, 1])
self.spectra_ax.tick_params(axis='both', which='both', labelsize=15)
self.spectra_ax.set_title(name, fontsize = 15)
self.spectra_ax.set_xlabel('Wavenumber (cm-1)', fontsize = 15)
self.spectra_ax.set_ylabel('Intensity (a.u.)', fontsize = 15)
self.spectra_ax.figure.canvas.draw()
self.removeSpecBtn.setText('Remove {}'.format(self.activeSpectrumName))
self.removeSpecBtn.setDisabled(False)
def save(self):
for index, db in enumerate(self.databases):
# pickling_on = open(db.title+'.dbpkl', 'wb')
# pickle.dump(db, pickling_on)
# pickling_on.close()
savename = os.path.join(self.path, db.title+'.db')
with open(savename, 'wb') as f:
dill.dump(db, f)
QtWidgets.QMessageBox.about(self, 'Done.', 'Saved {} database(s)'.format(len(self.databases)))
def updateDBSelectorList(self):
self.db_selector.clear()
if self.databases is not None:
for i in self.databases:
self.db_selector.addItem(i.title)
if len(