Commit 0b768ad2 authored by JosefBrandt's avatar JosefBrandt

First phase of refactoring

parent 8fc15b1e
......@@ -53,11 +53,12 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.setCentralWidget(self.widget)
self.parent = parent
self.dataset = dataset
self.particleContainer = dataset.particleContainer
self.datastats = DataStats(dataset)
self.editor = ParticleEditor(self.datastats, self)
self.additivePlot = None
# self.additivePlot = None
self.importWindow = None
self.polymerCheckBoxes = []
......@@ -67,8 +68,8 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.currentSpectrumIndex = 0
self.lastSpectrumInFocus = None
self.typeHistogram = ParticleTypeView(self)
self.typeHistogram.indexClicked.connect(self.getAdditivePlot)
self.typeHistogramPlot = ParticleTypeView(self)
# self.typeHistogramPlot.indexClicked.connect(self.getAdditivePlot)
self.sizeHistogramCanvas = FigureCanvas(Figure())
sizeHistGroup = QtWidgets.QGroupBox()
......@@ -106,7 +107,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
splitter1.addWidget(sizeHistGroup)
splitter2 = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
splitter2.addWidget(splitter1)
splitter2.addWidget(self.typeHistogram)
splitter2.addWidget(self.typeHistogramPlot)
splitter2.setSizes([300, 150])
self.navigationGroup = QtWidgets.QGroupBox('Navigate through polymers')
......@@ -164,32 +165,31 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.hqiSpinBox.setValue(75.0)
self.hqiSpinBox.setDecimals(1)
self.hqiSpinBox.setMinimum(0)
self.hqiSpinBox.setMaximum(100)
self.hqiSpinBox.setMaximumWidth(45)
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.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)
self.dispResultSpinBox.valueChanged.connect(self.updateHistograms)
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)
self.updateBtn.clicked.connect(self.applyHQIThresholdToResults)
# self.updateBtn.setDisabled(True)
optionsLayout.addRow(self.updateBtn)
self.optionsGroup.setLayout(optionsLayout)
self.optionsGroup.setMinimumWidth(175)
self.optionsGroup.setDisabled(True)
# self.optionsGroup.setDisabled(True)
self.resultScrollarea = QtWidgets.QScrollArea(self)
self.resultScrollarea.setFixedWidth(250)
......@@ -210,25 +210,24 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
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']
minHQI = self.dataset.resultParams['minHQI']
# compHQI = self.dataset.resultParams['compHQI']
if minHQI is not None:
self.hqiSpinBox.setValue(minHQI)
self.compHqiSpinBox.setValue(compHQI)
# self.compHqiSpinBox.setValue(compHQI)
self.createActions()
self.createMenus()
self.updateData()
self.initializeSpectraPlot()
self.applyHQIThresholdToResults()
def createActions(self):
self.loadTrueMatchAct = QtWidgets.QAction("Load &TrueMatch Results", self)
......@@ -241,7 +240,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.selOverlayAct = QtWidgets.QAction("&Selected Overlay", self)
self.fullOverlayAct = QtWidgets.QAction("&Full Overlay", self)
self.transpAct = QtWidgets.QAction("&Transparent Overlay")
self.transpAct = QtWidgets.QAction("&Transparent Overlay", self)
self.transpAct.triggered.connect(self.createPolymerOverlay)
self.hideLabelAct = QtWidgets.QAction('&Hide Polymer Numbers', self)
......@@ -276,7 +275,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.overlayActGroup = QtWidgets.QActionGroup(self.dispMenu)
self.overlayActGroup.setExclusive(True)
self.overlayActGroup.triggered.connect(self.createPolymerOverlay)
self.overlayActGroup.triggered.connect(self.updateHistogram)
self.overlayActGroup.triggered.connect(self.updateHistograms)
for act in [self.noOverlayAct, self.selOverlayAct, self.fullOverlayAct]:
self.dispMenu.addAction(act)
......@@ -312,63 +311,62 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
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
def initializeSpectraPlot(self): #formerly updateData(self)....
self.particleContainer.loadSpectraFromDisk()
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()
# 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(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())
def applyHQIThresholdToResults(self):
self.particleContainer.applyHQITresholdToParticles(self.hqiSpinBox.value())
self.createHistogramData()
# 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)
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
# if not self.datastats.createHistogramData():
# QtWidgets.QMessageBox.critical(self, 'Error', 'Inconsistent particle data. Please restore backup!')
# return
# self.particleContainer.updateTypeHistogram()
###Handle Checkboxes for all polymers...
self.menuLayout.removeWidget(self.resultScrollarea)
......@@ -381,7 +379,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
del i
self.showTotalSelector.setParent(None)
self.showTotalSelector.setDisabled(False)
self.showTotalSelector.stateChanged.connect(self.updateHistogram)
self.showTotalSelector.stateChanged.connect(self.updateHistograms)
del self.resultCheckBoxes
del self.resultCheckBoxesLayout
......@@ -391,17 +389,16 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
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()
uniquePolymers = self.particleContainer.getUniquePolymers()
for index, polymer in enumerate(uniquePolymers):
self.polymerCheckBoxes.append(QtWidgets.QCheckBox(self))
self.polymerCheckBoxes[index].setText(polymer)
......@@ -409,14 +406,12 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
if polymer in self.lastSelectedCheckBoxNames:
self.polymerCheckBoxes[index].setChecked(True)
self.polymerCheckBoxes[index].stateChanged.connect(self.updateHistogram)
self.polymerCheckBoxes[index].stateChanged.connect(self.updateHistograms)
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)
......@@ -436,7 +431,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
else:
self.displayNewPolymerType()
self.updateHistogram()
self.updateHistograms()
self.createPolymerOverlay()
def exportToExcel(self):
......@@ -451,7 +446,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
def updateSpecPlot(self, centerOn=True, highlightContour=True):
#draw Sample Spectrum
specIndex = self.currentSpectrumIndex
spectra = self.datastats.spectra
spectra = self.particleContainer.spectra
particlestats = self.datastats.getParticleStats()
self.spec_ax.axis("on")
self.spec_ax.clear()
......@@ -563,33 +558,29 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
self.currentSpectrumIndex = self.datastats.particles2spectra[self.currentParticleIndex][self.spectrumSelector.value()-1]
self.updateSpecPlot()
def updateHistogram(self):
def updateHistograms(self):
self.updateTypeHistogram()
self.updateSizeHistogram()
def updateTypeHistogram(self):
#draw the general histogram
colorList = []
abundancyList = []
labelList = []
for index, i in enumerate(self.datastats.typehistogram):
typeHistogram = self.particleContainer.getTypeHistogram()
for index, polymType in enumerate(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])
abundancyList.append(typeHistogram[polymType])
curColor = self.getColorFromName(polymType)
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)))
# print("abundancyList:", abundancyList)
# print("labelList:", labelList)
# print("colorList:", colorList)
self.typeHistogramPlot.updateTypeHistogram(list(zip(abundancyList, labelList, colorList)))
def updateSizeHistogram(self):
#general size histogram
self.sizeHist_ax.clear()
self.sizeHist_ax.axis('on')
......@@ -648,7 +639,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
if ok:
self.datastats.colorSeed = text
self.datastats.dataset.colorSeed = text
self.updateHistogram()
self.updateHistograms()
self.createPolymerOverlay()
def getColorFromName(self, name, base255=True):
......@@ -673,7 +664,7 @@ class ParticleAnalysis(QtWidgets.QMainWindow):
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)
colorList = [QtGui.QColor(255, 255, 255, alpha=50)]*len(self.datastats.particleContainer)
legendItems = []
for index, indexList in enumerate(self.datastats.indices):
......
......@@ -37,7 +37,7 @@ class ExpExcelDialog(QtWidgets.QDialog):
self.datastats = datastats
self.particles = self.datastats.getParticleStats()
self.polymers = self.datastats.particleResults
self.polymers = self.datastats.particleContainer
self.additives = self.datastats.currentAdditives
self.hqis = self.datastats.hqis
......@@ -262,8 +262,8 @@ class ParticleTypeView(QtWidgets.QScrollArea):
self.setAlignment(QtCore.Qt.AlignHCenter)
self.widgets = []
def updateTypes(self, types):
print("Updating polymer type view", flush=True)
def updateTypeHistogram(self, types):
# print("Updating polymer type view", flush=True)
for pi in self.widgets:
self.indicatorbox.removeWidget(pi)
pi.setParent(None)
......@@ -277,7 +277,7 @@ class ParticleTypeView(QtWidgets.QScrollArea):
for index, entry in enumerate(types):
num, text, color = entry
print("num, text, color:", num, text, color, flush=True)
# print("num, text, color:", num, text, color, flush=True)
pi = ParticleIndicator(num, numtotal, color, text)
self.indicatorbox.addWidget(pi)
pi.clicked.connect(getIndexFunction(index))
......
......@@ -35,13 +35,16 @@ from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as Navigatio
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')
logpath = QtCore.QStandardPaths.writableLocation(
QtCore.QStandardPaths.AppLocalDataLocation)
self.path = os.path.join(logpath, 'databases')
self.importPath = self.path
if not os.path.exists(self.path):
os.mkdir(self.path)
......
......@@ -21,9 +21,186 @@ If not, see <https://www.gnu.org/licenses/>.
import os
import numpy as np
import operator
from dataset import loadData, recursiveDictCompare
from PyQt5 import QtWidgets
import importSpectra
try:
from dataset import loadData, recursiveDictCompare
print('exported dataset methods from datastats')
except:
print('failed exported dataset methods from datastats')
class ParticleContainer(object):
def __init__(self, parent):
self.parent = parent
self.particles = []
self.spectra = None
self.typeHistogram = None
def initializeParticles(self, numParticles):
self.particles = []
for i in range(numParticles):
self.particles.append(Particle(i))
def loadSpectraFromDisk(self, fname):
try:
self.spectra = self.tryLoadingNumpySpecFile()
except ImportError:
fname = QtWidgets.QFileDialog.getOpenFileName(self.parent, 'Select Spectra File', self.dataset.path, 'text file (*.txt)')[0]
try:
self.spectra, spectraNames = importSpectra.importWITecSpectra(fname)
except ImportError:
try:
self.spectra, spectraNames = importSpectra.importRenishawSpectra(fname)
except ImportError:
self.spectra, spectraNames = importSpectra.importPerkinElmerSpectra(fname)
if self.spectra is None:
raise ImportError
def tryLoadingNumpySpecFile(self):
specPath = self.parent.getSpectraFileName()
if os.path.exists(specPath):
return np.load(specPath)
else:
raise ImportError
def setParticlecontours(self, contours):
assert len(self.particles) == len(contours)
for index, particle in enumerate(self.particles):
particle.contour = contours[index]
def setParticleStats(self, particlestats):
assert len(self.particles) == len(particlestats)
#particlestats is list of [long, short, longellipse, shortellipse, cv2.contourArea(cnt)]
for index, particle in enumerate(self.particles):
particle.longSize_box = particlestats[0]
particle.shortSize_box = particlestats[1]
particle.longSize_ellipse = particlestats[2]
particle.shortSize_ellipse = particlestats[3]
particle.area = particlestats[4]
def applyHQITresholdToParticles(self, minHQI):
for particle in self.particles:
particle.applyHQITresholdToMeasurements(minHQI)
def getNumberOfParticles(self):
return len(self.particles)
def getMeasurementPixelCoords(self):
coords = []
for particle in self.particles:
for measurement in particle.measurements:
coords.append([measurement.pixelcoord_x, measurement.pixelcoord_y])
return coords
def getListOfParticleAssignments(self):
particleAssignments = []
for particle in self.particles:
particleAssignments.append(particle.getParticleAssignment())
return particleAssignments
def getListOfParticleSizes(self):
particleSizes = []
def getTypeHistogram(self):
uniquePolymers = self.getUniquePolymers()
typehistogram = {i: 0 for i in uniquePolymers}
for assignment in self.getListOfParticleAssignments():
typehistogram[assignment] += 1
##sort typehistogram, it will be converted into a list!!
sorted_typehistogram = sorted(typehistogram.items(), key = operator.itemgetter(1), reverse = True)
#convert back to dict
final_typehistogram = {i[0]: i[1] for i in sorted_typehistogram}
return final_typehistogram
def getUniquePolymers(self):
return np.unique(self.getListOfParticleAssignments())
class Particle(object):
def __init__(self, index):
self.index = index
self.longSize_ellipse = None #TODO: IS CURRENTLY PIXEL SIZE, CONVERT TO MICRONS
self.shortSize_ellipse = None
self.longSize_box = None
self.shortSize_box = None
self.area = None
self.contour = None
self.measurements = []
def addEmptyMeasurement(self):
self.measurements.append(Measurement())
indexOfNewMeasurment = len(self.measurements)-1
return indexOfNewMeasurment
def setMeasurementScanIndex(self, indexOfMeasurment, scanIndex):
self.measurements[indexOfMeasurment].ramanScanIndex = scanIndex
def setMeasurementPixelCoords(self, indexOfMeasurment, x, y):
self.measurements[indexOfMeasurment].pixelcoord_x= x
self.measurements[indexOfMeasurment].pixelcoord_y = y
def getParticleAssignment(self):
if self.measurementsYieldSameResult():
return self.measurements[0].getAssignment()
else:
print(f'ERROR: Particle with index {self.index} has non-unique analysis results!')
return None
def getParticleSize(self):
if self.longSize_ellipse is not None:
return self.longSize_ellipse
elif self.longSize_box is not None:
return self.longSize_box
else:
print(f'Error, particle size requested, but not yet set.\nParticle Index is {self.index}')
raise ValueError
def measurementsYieldSameResult(self):
allResults = [meas.getAssignment() for meas in self.measurements]
if len(np.unique(allResults)) == 1:
return True
elif len(np.unique(allResults)) > 1:
return False
def applyHQITresholdToMeasurements(self, minHQI):
for measurement in self.measurements:
measurement.applyHQITrehsold(minHQI)
class Measurement(object):
def __init__(self):
self.ramanScanIndex = None
self.pixelcoord_x= None
self.pixelcoord_y = None
self.assignment_orig = 'Not Evaluated'
self.assignment_afterHQI = None
self.hqi = None
def setAssignemtAndHQI(self, assignment, hqi):
self.assignment_orig = assignment
self.assignment_afterHQI = 'unknown'
self.hqi = hqi
self.applyHQIThreshold()
def applyHQIThreshold(self, minHQI=0):
if self.hqi >= minHQI:
self.assignment_afterHQI = self.assignment_orig
else:
self.assignment_afterHQI = 'unknown'
def getAssignment(self):
if self.assignment_afterHQI is None:
return self.assignment_orig
else:
return self.assignment_afterHQI
def readDataStats(fname):
ds = loadData(fname)
datastats = DataStats(ds)
......@@ -41,7 +218,7 @@ class DataStats(object):
self.spectraResults = None #entire List of all spectra assignments
self.additiveResults = None #entire List of all additives
self.particleResults = None #final assignment for each particle
self.ParticleContainer = None #final assignment for each particle
self.currentPolymers = None #list of polymers after setting entries with low hqi to unknown