analysisview.py 28.7 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
#!/usr/bin/env python3
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
"""
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
24
import numpy as np
JosefBrandt's avatar
 
JosefBrandt committed
25
import sys
JosefBrandt's avatar
 
JosefBrandt committed
26
import time
27 28 29 30 31

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar

JosefBrandt's avatar
JosefBrandt committed
32
from .excelexport import ExpExcelDialog
JosefBrandt's avatar
 
JosefBrandt committed
33
from .analysisplots import TypeHistogramView, SpectraPlot
JosefBrandt's avatar
 
JosefBrandt committed
34
from .loadresults import LoadTrueMatchResults
35
from .database import DataBaseWindow
JosefBrandt's avatar
 
JosefBrandt committed
36
from .colorlegend import getColorFromNameWithSeed
JosefBrandt's avatar
 
JosefBrandt committed
37

Hackmet's avatar
Hackmet committed
38
try:
39
    from .sqlexport import SQLExport
Hackmet's avatar
Hackmet committed
40 41 42
    sqlEnabled = True
except:
    sqlEnabled = False
43 44


Hackmet's avatar
Hackmet committed
45
class ParticleAnalysis(QtWidgets.QMainWindow):
46 47
    def __init__(self, dataset, viewparent=None):
        super(ParticleAnalysis, self).__init__(viewparent)
JosefBrandt's avatar
JosefBrandt committed
48
        self.resize(1680, 1050)
49 50
        self.setWindowTitle('Results of polymer analysis')
        self.layout = QtWidgets.QHBoxLayout()
Hackmet's avatar
Hackmet committed
51 52 53 54
        self.widget = QtWidgets.QWidget()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)
        
55
        self.viewparent = viewparent
JosefBrandt's avatar
JosefBrandt committed
56 57
        self.dataset = dataset
        self.particleContainer = dataset.particleContainer
58 59
        
        self.importWindow = None
60
        
61 62 63 64 65 66
        self.polymerCheckBoxes = []
        self.lastSelectedCheckBoxNames = []
        
        self.currentParticleIndex = 0
        self.currentSpectrumIndex = 0
        
JosefBrandt's avatar
 
JosefBrandt committed
67
        self.typeHistogramPlot = TypeHistogramView(self)
68 69 70 71 72 73 74 75 76
        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)
Hackmet's avatar
Hackmet committed
77
        histNavigation.setFixedWidth(50)
78 79 80 81
        sizeHistLayout.addWidget(histNavigation)
        sizeHistLayout.addWidget(self.sizeHistogramCanvas)
        sizeHistGroup.setLayout(sizeHistLayout)
        
JosefBrandt's avatar
 
JosefBrandt committed
82
        self.specPlot = SpectraPlot(self.dataset)
83 84
        
        splitter1 = QtWidgets.QSplitter(QtCore.Qt.Vertical)
JosefBrandt's avatar
 
JosefBrandt committed
85
        splitter1.addWidget(self.specPlot)
86 87 88
        splitter1.addWidget(sizeHistGroup)
        splitter2 = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
        splitter2.addWidget(splitter1)
JosefBrandt's avatar
JosefBrandt committed
89
        splitter2.addWidget(self.typeHistogramPlot)
90 91
        splitter2.setSizes([300, 150])
       
JosefBrandt's avatar
 
JosefBrandt committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
        referenceGroup = QtWidgets.QGroupBox('Reference Spectra')
        referenceLayout = QtWidgets.QHBoxLayout()
        
        self.refSelector = QtWidgets.QComboBox()
        self.refSelector.setMinimumWidth(200)
        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)
        referenceLayout.addStretch()
        referenceGroup.setLayout(referenceLayout)
        

108 109 110
        self.navigationGroup = QtWidgets.QGroupBox('Navigate through polymers')
        self.navigationGroup.setDisabled(True)
        navigationLayout = QtWidgets.QHBoxLayout()
JosefBrandt's avatar
 
JosefBrandt committed
111
        self.specNumberSelector = QtWidgets.QSpinBox()
112
        self.specNumberSelector.setMinimumWidth(100)
JosefBrandt's avatar
 
JosefBrandt committed
113
        self.specNumberSelector.setMinimum(1)
JosefBrandt's avatar
JosefBrandt committed
114 115
        numSpectra = self.particleContainer.getNumberOfMeasurements()
        self.specNumberSelector.setMaximum(numSpectra)
JosefBrandt's avatar
 
JosefBrandt committed
116
        
JosefBrandt's avatar
 
JosefBrandt committed
117
        self.jumpToSpecBtn = QtWidgets.QPushButton('Jump To Spectrum of Number:')
118
        self.jumpToSpecBtn.released.connect(self.jumpToIndicatedSpectrum)
JosefBrandt's avatar
 
JosefBrandt committed
119
        
JosefBrandt's avatar
 
JosefBrandt committed
120 121
        self.typeSelectorCombo = QtWidgets.QComboBox()
        self.typeSelectorCombo.setMinimumWidth(150)
JosefBrandt's avatar
 
JosefBrandt committed
122
        self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector)
123
        self.particleSelector = QtWidgets.QSpinBox()
JosefBrandt's avatar
 
JosefBrandt committed
124
        self.particleSelector.valueChanged.connect(self.setParticleSelector)
JosefBrandt's avatar
 
JosefBrandt committed
125 126
        self.particleNumberLabel = QtWidgets.QLabel('of xx particles; ')
        
127
        self.spectrumSelector = QtWidgets.QSpinBox()
JosefBrandt's avatar
 
JosefBrandt committed
128
        self.spectrumSelector.valueChanged.connect(self.setSpecSelector)
JosefBrandt's avatar
 
JosefBrandt committed
129
        self.spectrumNumberLabel = QtWidgets.QLabel('of xx spectra')
130 131 132 133 134
        for spinbox in [self.particleSelector, self.spectrumSelector]:
            spinbox.setMinimum(1)
            spinbox.setSingleStep(1)
            spinbox.setValue(1)
        
JosefBrandt's avatar
 
JosefBrandt committed
135 136
        navigationLayout.addWidget(self.jumpToSpecBtn)
        navigationLayout.addWidget(self.specNumberSelector)
137
        navigationLayout.addWidget(QtWidgets.QLabel('Select Polymer Type:'))
JosefBrandt's avatar
 
JosefBrandt committed
138
        navigationLayout.addWidget(self.typeSelectorCombo)
139 140 141
        navigationLayout.addStretch()
        navigationLayout.addWidget(QtWidgets.QLabel('Select Particle'))
        navigationLayout.addWidget(self.particleSelector)
JosefBrandt's avatar
 
JosefBrandt committed
142 143
        navigationLayout.addWidget(self.particleNumberLabel)
        navigationLayout.addStretch()
144 145
        navigationLayout.addWidget(QtWidgets.QLabel('Select Spectrum'))
        navigationLayout.addWidget(self.spectrumSelector)
JosefBrandt's avatar
 
JosefBrandt committed
146
        navigationLayout.addWidget(self.spectrumNumberLabel)
147 148 149 150 151
        
        self.navigationGroup.setLayout(navigationLayout)
        
        topLayout = QtWidgets.QHBoxLayout()
        topLayout.addWidget(self.navigationGroup)
Hackmet's avatar
Hackmet committed
152
        topLayout.addWidget(referenceGroup)
153
        
JosefBrandt's avatar
 
JosefBrandt committed
154
        viewLayout = QtWidgets.QVBoxLayout()
155 156 157 158
        viewLayout.addLayout(topLayout)
        viewLayout.addWidget(splitter2)
        viewLayout.setStretch(1, 1)

JosefBrandt's avatar
 
JosefBrandt committed
159
        self.optionsGroup = QtWidgets.QGroupBox('Set HQI Threshold')
160 161 162 163 164
        optionsLayout = QtWidgets.QFormLayout()
        
        self.hqiSpinBox = QtWidgets.QDoubleSpinBox()
        self.hqiSpinBox.setDecimals(1)
        self.hqiSpinBox.setMinimum(0)
JosefBrandt's avatar
JosefBrandt committed
165
        self.hqiSpinBox.setMaximum(100)
JosefBrandt's avatar
 
JosefBrandt committed
166
        self.hqiSpinBox.setMaximumWidth(100)
167 168 169
        minHQI = self.dataset.resultParams['minHQI']
        if minHQI is not None:
            self.hqiSpinBox.setValue(minHQI)
JosefBrandt's avatar
 
JosefBrandt committed
170
        self.hqiSpinBox.valueChanged.connect(self.applyHQIThresholdToResults)
JosefBrandt's avatar
JosefBrandt committed
171 172 173
        minHQI = self.dataset.resultParams['minHQI']
        if minHQI is not None:
            self.hqiSpinBox.setValue(minHQI)
JosefBrandt's avatar
 
JosefBrandt committed
174
        optionsLayout.addRow(QtWidgets.QLabel('minimum HQI:'), self.hqiSpinBox)
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
        
        self.optionsGroup.setLayout(optionsLayout)
        self.optionsGroup.setMinimumWidth(175)
        
        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)
JosefBrandt's avatar
 
JosefBrandt committed
198 199
        
        self.menuLayout = QtWidgets.QVBoxLayout()
200 201 202 203 204 205
        self.menuLayout.addWidget(self.optionsGroup)
        self.menuLayout.addWidget(self.resultScrollarea)
        
        self.layout.addLayout(self.menuLayout)
        self.layout.addLayout(viewLayout)
        
206

207
         
Hackmet's avatar
Hackmet committed
208 209
        self.createActions()
        self.createMenus()
JosefBrandt's avatar
JosefBrandt committed
210
        self.applyHQIThresholdToResults()
JosefBrandt's avatar
 
JosefBrandt committed
211 212
        self.createHistogramData()
        self.initializeSpecPlot()
JosefBrandt's avatar
 
JosefBrandt committed
213
        self.setTypeSelector()
214
        self.updateHistogramsAndContours()
Hackmet's avatar
Hackmet committed
215 216 217 218 219
    
    def createActions(self):
        self.loadTrueMatchAct = QtWidgets.QAction("Load &TrueMatch Results", self)
        self.loadTrueMatchAct.triggered.connect(self.importTrueMatchResults)
        
JosefBrandt's avatar
JosefBrandt committed
220 221
        self.loadSpectraAct = QtWidgets.QAction("Load &Spectra", self)
        self.loadSpectraAct.triggered.connect(self.initializeSpecPlot)
Hackmet's avatar
Hackmet committed
222 223 224 225 226
        
        self.noOverlayAct = QtWidgets.QAction("&No Overlay", self)
        self.selOverlayAct = QtWidgets.QAction("&Selected Overlay", self)
        self.fullOverlayAct = QtWidgets.QAction("&Full Overlay", self)
        
JosefBrandt's avatar
JosefBrandt committed
227
        self.transpAct = QtWidgets.QAction("&Transparent Overlay", self)
228
        self.transpAct.triggered.connect(self.updateContourColors)
Hackmet's avatar
Hackmet committed
229 230 231 232 233 234 235 236 237 238 239 240 241
            
        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)
JosefBrandt's avatar
JosefBrandt committed
242
        
243 244 245
        self.removeTinyParticlesAct = QtWidgets.QAction("&Remove not unknown Particles < 1 µm")
        self.removeTinyParticlesAct.triggered.connect(self.removeTinyParticles)

Hackmet's avatar
Hackmet committed
246 247 248 249 250 251 252 253 254 255 256 257
        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):
JosefBrandt's avatar
JosefBrandt committed
258 259
        self.importMenu = QtWidgets.QMenu("&Import Spectra and Results")
        self.importMenu.addActions([self.loadSpectraAct, self.loadTrueMatchAct])
Hackmet's avatar
Hackmet committed
260 261 262 263
        
        self.dispMenu = QtWidgets.QMenu("&Display", self)
        self.overlayActGroup = QtWidgets.QActionGroup(self.dispMenu)
        self.overlayActGroup.setExclusive(True)
264
        self.overlayActGroup.triggered.connect(self.updateContourColors)
265
        self.overlayActGroup.triggered.connect(self.updateHistogramsAndContours)
Hackmet's avatar
Hackmet committed
266 267 268 269 270 271 272 273
        
        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])
        
274 275 276
        self.toolMenu = QtWidgets.QMenu("&Tools")
        self.toolMenu.addAction(self.removeTinyParticlesAct)
        self.toolMenu.addAction(self.databaseAct)
Hackmet's avatar
Hackmet committed
277 278 279 280 281 282 283
        
        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)
284
        self.menuBar().addMenu(self.toolMenu)
Hackmet's avatar
Hackmet committed
285
        self.menuBar().addMenu(self.exportMenu)
JosefBrandt's avatar
JosefBrandt committed
286 287 288 289 290
    
    def keyPressEvent(self, event):
        if event.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter]:
            self.jumpToIndicatedSpectrum()
    
Hackmet's avatar
Hackmet committed
291 292 293 294 295 296 297 298 299 300 301
    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:
Hackmet's avatar
Hackmet committed
302
            self.refSelector.addItem('')
Hackmet's avatar
Hackmet committed
303 304 305 306
            self.refSelector.addItems(self.dbWin.activeDatabase.spectraNames)
            self.refSelector.setDisabled(False)
            
    def importTrueMatchResults(self):
JosefBrandt's avatar
JosefBrandt committed
307 308 309
        if self.importWindow is not None:
            del self.importWindow
        
JosefBrandt's avatar
 
JosefBrandt committed
310
        self.importWindow = LoadTrueMatchResults(self.particleContainer, self)
JosefBrandt's avatar
JosefBrandt committed
311
#        self.importWindow.exec()
JosefBrandt's avatar
JosefBrandt committed
312
        
313
    @QtCore.pyqtSlot()
JosefBrandt's avatar
JosefBrandt committed
314
    def applyHQIThresholdToResults(self):
JosefBrandt's avatar
 
JosefBrandt committed
315 316 317 318
        hqi = self.hqiSpinBox.value()
        self.particleContainer.applyHQITresholdToParticles(hqi)
        self.dataset.resultParams['minHQI'] = hqi
        self.dataset.save()
JosefBrandt's avatar
JosefBrandt committed
319
        self.createHistogramData()
320
        self.updateHistogramsAndContours()
JosefBrandt's avatar
 
JosefBrandt committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
     
    def updateHistogramsAndContours(self):  
        self.createHistogramData()
        t0 = time.time()
        self.updateTypeHistogram()
        print('update type hist: {} ms'.format(round((time.time()-t0)*1000)))
        t0 = time.time()
        self.updateSizeHistogram()
        print('update size hist: {} ms'.format(round((time.time()-t0)*1000)))
        t0 = time.time()
        self.updateContourColors()
        print('update contours: {} ms'.format(round((time.time()-t0)*1000)))
        t0 = time.time()
        self.updateLegend()
        print('update legend: {} ms'.format(round((time.time()-t0)*1000)))
        self.dataset.save()
        
338
    def createHistogramData(self):
JosefBrandt's avatar
 
JosefBrandt committed
339 340
        self.lastSelectedCheckBoxNames = self.getSelectedPolymers()
        
341 342 343 344 345 346 347 348 349 350 351
        ###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)
352
        self.showTotalSelector.stateChanged.connect(self.updateHistogramsAndContours)
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
        
        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)
JosefBrandt's avatar
JosefBrandt committed
368
        
369
        #generate new checkboxes 
JosefBrandt's avatar
 
JosefBrandt committed
370
        self.polymerCheckBoxes = []#
JosefBrandt's avatar
JosefBrandt committed
371
        uniquePolymers = self.particleContainer.getUniquePolymers()
372
        for index, polymer in enumerate(uniquePolymers):
373 374 375 376 377 378
            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)
            
379
            self.polymerCheckBoxes[index].stateChanged.connect(self.updateHistogramsAndContours)
380 381 382 383 384 385

        self.resultCheckBoxesLayout.addStretch()
        self.resultCheckBoxes.setLayout(self.resultCheckBoxesLayout)
        self.layout_SArea.addWidget(self.resultCheckBoxes)
        self.menuLayout.addWidget(self.resultScrollarea)
        
Hackmet's avatar
Hackmet committed
386 387 388
        self.expExcelAct.setDisabled(False)
        if sqlEnabled:
            self.expSQLAct.setDisabled(False)
389 390
        
        self.navigationGroup.setEnabled(True)
JosefBrandt's avatar
 
JosefBrandt committed
391
        self.typeSelectorCombo.currentIndexChanged.disconnect()
JosefBrandt's avatar
 
JosefBrandt committed
392 393
        self.typeSelectorCombo.clear()
        self.typeSelectorCombo.addItems(uniquePolymers)
JosefBrandt's avatar
 
JosefBrandt committed
394 395
        self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector)
        
JosefBrandt's avatar
JosefBrandt committed
396 397
        numSpectra = self.particleContainer.getNumberOfMeasurements()
        self.specNumberSelector.setMaximum(numSpectra)
JosefBrandt's avatar
 
JosefBrandt committed
398
        
JosefBrandt's avatar
 
JosefBrandt committed
399 400 401
    def initializeSpecPlot(self):
        self.specPlot.loadSpectraAndInitializeSpecPlot()
        self.updateSpecPlot()
402
    
JosefBrandt's avatar
 
JosefBrandt committed
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
    def updateTypeHistogram(self):
        colorList = []
        abundancyList = []
        labelList = []
        typeHistogram = self.particleContainer.getTypeHistogram()
        for index, polymType in enumerate(typeHistogram):
            if not self.selOverlayAct.isChecked() or self.polymerCheckBoxes[index].isChecked():
                text = f'{typeHistogram[polymType]} x {polymType}'
                labelList.append(text)
                abundancyList.append(typeHistogram[polymType])
                curColor = getColorFromNameWithSeed(polymType, self.dataset.colorSeed)
                colorList.append(QtGui.QColor(*curColor))
            
        self.typeHistogramPlot.updateTypeHistogram(list(zip(abundancyList, labelList, colorList)))
    
    def updateSizeHistogram(self):
        if self.particleContainer.getNumberOfParticles() > 0:
            #general size histograms
            self.sizeHist_ax.clear()
            self.sizeHist_ax.axis('on')
            self.bins = np.logspace(0.1, 3, 20)
            self.sizes = self.particleContainer.getSizesOfAllParticles()
                
            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')
                
            for polymType in self.getSelectedPolymers():
                sizes = self.particleContainer.getSizesOfParticleType(polymType)
                sizehist = np.histogram(sizes, self.bins)
                color = getColorFromNameWithSeed(polymType, self.dataset.colorSeed, base255=False)
                self.sizeHist_ax.semilogx(self.totalhistx, sizehist[0], label=polymType, color=color)    
            
            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()
    
454
    def updateSpecPlot(self):
JosefBrandt's avatar
JosefBrandt committed
455
        particleSize = np.round(self.particleContainer.getSizeOfParticleByIndex(self.currentParticleIndex))
JosefBrandt's avatar
 
JosefBrandt committed
456
        hqi = self.particleContainer.getHQIOfSpectrumIndex(self.currentSpectrumIndex)
JosefBrandt's avatar
JosefBrandt committed
457
        assignment = self.particleContainer.getParticleAssignmentByIndex(self.currentParticleIndex)
458 459
        color = self.particleContainer.getParticleColorByIndex(self.currentParticleIndex)
        self.specPlot.updateParticleSpectrum(self.currentSpectrumIndex, assignment, particleSize, hqi, color)
JosefBrandt's avatar
 
JosefBrandt committed
460
            
Hackmet's avatar
Hackmet committed
461 462 463
        if self.refSelector.isEnabled() and self.refSelector.currentText() != '':
            refID = self.dbWin.activeDatabase.spectraNames.index(self.refSelector.currentText())
            ref = self.dbWin.activeDatabase.spectra[refID]
JosefBrandt's avatar
 
JosefBrandt committed
464
            self.specPlot.updateReferenceSpectrum(ref[:, 0], ref[:, 1])
JosefBrandt's avatar
 
JosefBrandt committed
465
    
466
    def updateContourColors(self):
467
        contours = self.viewparent.contourItems
JosefBrandt's avatar
 
JosefBrandt committed
468
        alpha = (64 if self.transpAct.isChecked() else 255)
JosefBrandt's avatar
 
JosefBrandt committed
469 470 471 472 473 474 475 476 477 478 479
        selectedPolymers = self.getSelectedPolymers()

        for particleIndex, contour in enumerate(contours):
            assignment = self.particleContainer.getParticleAssignmentByIndex(particleIndex)
            hidden = self.noOverlayAct.isChecked() or (not self.fullOverlayAct.isChecked() and assignment not in selectedPolymers)
            color = getColorFromNameWithSeed(assignment, self.dataset.colorSeed)
            color = QtGui.QColor(color[0], color[1], color[2], alpha=alpha)
            
            contour.setHidden(hidden)
            contour.setColor(color)
            contour.update()
480
    
JosefBrandt's avatar
 
JosefBrandt committed
481 482 483 484 485 486 487 488 489
    def updateLegend(self):
        if not self.noOverlayAct.isChecked():
            legendItems = []
            selectedPolymers = self.getSelectedPolymers()
            for polymer in self.particleContainer.getUniquePolymers():
                if self.fullOverlayAct.isChecked() or polymer in selectedPolymers:                
                    color = getColorFromNameWithSeed(polymer, self.dataset.colorSeed)
                    color = QtGui.QColor(color[0], color[1], color[2], 255)
                    legendItems.append((polymer, color))
490
            self.viewparent.updateLegend(legendItems)
JosefBrandt's avatar
 
JosefBrandt committed
491 492 493

    def getSelectedPolymers(self):
        return [checkbox.text() for checkbox in self.polymerCheckBoxes if checkbox.isChecked()]
JosefBrandt's avatar
 
JosefBrandt committed
494 495 496 497 498 499 500 501
     
    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()
JosefBrandt's avatar
 
JosefBrandt committed
502
    
JosefBrandt's avatar
 
JosefBrandt committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
    def setParticleSelector(self):
        assignment = self.typeSelectorCombo.currentText()
        if assignment != '':
            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):
        specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex)
        self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1]
518
        self.specNumberSelector.setValue(self.currentSpectrumIndex+1)
JosefBrandt's avatar
 
JosefBrandt committed
519 520
        
        self.updateSpecPlot()
521 522
        self.jumpToIndicatedSpectrum()
    
JosefBrandt's avatar
JosefBrandt committed
523
    def setWidgetsToNewParticleIndex(self, particleIndex):
524 525 526
        try:
            self.particleSelector.valueChanged.disconnect()
            self.spectrumSelector.valueChanged.disconnect()
JosefBrandt's avatar
 
JosefBrandt committed
527
            self.typeSelectorCombo.currentIndexChanged.disconnect()
528
        except TypeError:
JosefBrandt's avatar
JosefBrandt committed
529
            pass #signals were not connected, nothing to disconnect...
530
        
JosefBrandt's avatar
 
JosefBrandt committed
531
        assignment = self.particleContainer.getParticleAssignmentByIndex(particleIndex)
JosefBrandt's avatar
 
JosefBrandt committed
532
        self.typeSelectorCombo.setCurrentText(assignment)
JosefBrandt's avatar
 
JosefBrandt committed
533
        
JosefBrandt's avatar
 
JosefBrandt committed
534
        self.currentParticleIndex = particleIndex
JosefBrandt's avatar
 
JosefBrandt committed
535
        partIndices = self.particleContainer.getIndicesOfParticleType(assignment)
JosefBrandt's avatar
 
JosefBrandt committed
536
        self.particleSelector.setMaximum(len(partIndices))
537
        self.particleNumberLabel.setText(f'of {len(partIndices)} particles; ')
JosefBrandt's avatar
 
JosefBrandt committed
538
        
JosefBrandt's avatar
 
JosefBrandt committed
539
        self.particleSelector.setValue(partIndices.index(particleIndex)+1)
540
        
JosefBrandt's avatar
 
JosefBrandt committed
541
        specIndices = self.particleContainer.getSpectraIndicesOfParticle(self.currentParticleIndex)
JosefBrandt's avatar
 
JosefBrandt committed
542
        self.spectrumSelector.setValue(1)
JosefBrandt's avatar
 
JosefBrandt committed
543 544
        self.spectrumSelector.setMaximum(len(specIndices))        
        self.spectrumNumberLabel.setText(f'of {len(specIndices)} spectra')
JosefBrandt's avatar
 
JosefBrandt committed
545
        
JosefBrandt's avatar
 
JosefBrandt committed
546 547
        self.currentSpectrumIndex = specIndices[self.spectrumSelector.value()-1]
        self.specNumberSelector.setValue(self.currentSpectrumIndex+1)
548
        self.updateSpecPlot()
JosefBrandt's avatar
 
JosefBrandt committed
549 550 551 552 553
        
        self.typeSelectorCombo.currentIndexChanged.connect(self.setTypeSelector)
        self.spectrumSelector.valueChanged.connect(self.setSpecSelector)
        self.particleSelector.valueChanged.connect(self.setParticleSelector)
        
JosefBrandt's avatar
JosefBrandt committed
554
    def jumpToIndicatedSpectrum(self):
JosefBrandt's avatar
 
JosefBrandt committed
555 556
        self.currentSpectrumIndex = self.specNumberSelector.value()-1
        self.currentParticleIndex = self.particleContainer.getParticleIndexContainingSpecIndex(self.currentSpectrumIndex)
JosefBrandt's avatar
JosefBrandt committed
557
        self.viewparent.centerOnRamanIndex(self.currentSpectrumIndex)
JosefBrandt's avatar
JosefBrandt committed
558 559
        self.viewparent.highLightContour(self.currentParticleIndex)
        self.updateSpecPlot()
560 561
    
    def darkenBackground(self):
562
        self.viewparent.darkenPixmap = self.darkenAct.isChecked()
563
        
Hackmet's avatar
Hackmet committed
564
        if self.darkenAct.isChecked():
565 566
            self.viewparent.scene().setBackgroundBrush(QtGui.QColor(5, 5, 5))
            self.viewparent.item.setOpacity(0.2)
567
        else:
568 569
            self.viewparent.scene().setBackgroundBrush(QtCore.Qt.darkGray)
            self.viewparent.item.setOpacity(1)
570 571

    def updateColorSeed(self):
JosefBrandt's avatar
 
JosefBrandt committed
572
        text, ok = QtWidgets.QInputDialog.getText(self, 'Color Seed', 'Enter New Seed here', text=self.dataset.colorSeed)
573
        if ok:
JosefBrandt's avatar
 
JosefBrandt committed
574
            self.dataset.colorSeed = text
575 576 577 578 579 580 581 582 583 584 585 586
            self.updateHistogramsAndContours()
    
    def removeTinyParticles(self):
        indices = []
        for particle in self.particleContainer.particles:
            if particle.getParticleAssignment() != 'unknown' and particle.getParticleSize() < 1:
                indices.append(particle.index)
        
        indices = sorted(indices, reverse=True)
        numIndices = len(indices)
        
        for index, partIndex in enumerate(indices):
JosefBrandt's avatar
 
JosefBrandt committed
587
            self.setWidgetsToNewParticleIndex(partIndex)
JosefBrandt's avatar
JosefBrandt committed
588 589
            assignment = self.particleContainer.getParticleAssignmentByIndex(partIndex)
            specIndices = self.particleContainer.getSpectraIndicesOfParticle(partIndex)
JosefBrandt's avatar
 
JosefBrandt committed
590 591
            self.viewparent.highLightContour(partIndex)
            self.viewparent.centerOnRamanIndex(specIndices[0])
592
            reply = QtWidgets.QMessageBox.question(self, f'Particle {index+1} of {numIndices}',
JosefBrandt's avatar
 
JosefBrandt committed
593
                                f"Do you want to remove that particle? Type = {assignment}",
594 595
                                QtWidgets.QMessageBox.Yes | 
                                QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.No)
596
            
597 598 599 600 601 602 603 604 605 606 607
            if reply == QtWidgets.QMessageBox.Yes:
                self.viewparent.removeParticleContour(partIndex)
                self.particleContainer.removeParticles([partIndex])
                
            elif reply == QtWidgets.QMessageBox.Cancel:
                self.particleContainer.resetParticleIndices()
                self.viewparent.resetContourIndices()
                return
        
        self.particleContainer.resetParticleIndices()
        self.viewparent.resetContourIndices()
JosefBrandt's avatar
 
JosefBrandt committed
608
        self.updateHistogramsAndContours()
609
    
610
    def show_hide_labels(self):
Hackmet's avatar
Hackmet committed
611
        hidden = self.hideLabelAct.isChecked()
612
        for scanIndicator in self.viewparent.ramanscanitems:
613 614
            scanIndicator.hidden = hidden
            scanIndicator.update()
JosefBrandt's avatar
 
JosefBrandt committed
615 616
    
    def exportToExcel(self):
JosefBrandt's avatar
JosefBrandt committed
617
        expWin = ExpExcelDialog(self.dataset)
JosefBrandt's avatar
 
JosefBrandt committed
618 619 620
        expWin.exec()
            
    def exportToSQL(self):
621 622 623 624 625 626 627 628
        if len(self.dataset.resultsUploadedToSQL) > 0:
           reply = QtWidgets.QMessageBox.question(self, 'Warning!',
                                "The following results were already uploaded:\n\n{}\n\nContinue?".format('\n'.join(self.dataset.resultsUploadedToSQL)),
                                QtWidgets.QMessageBox.Yes | 
                                QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
           if reply == QtWidgets.QMessageBox.Yes:
                sqlexp = SQLExport(self.dataset)
                sqlexp.exec()
629 630
    
    def closeEvent(self, event):
JosefBrandt's avatar
 
JosefBrandt committed
631
        for window in [self.importWindow, self.dbWin]:
JosefBrandt's avatar
JosefBrandt committed
632 633 634
            if window is not None:
                window.close()
                
635
        self.viewparent.imparent.particelAnalysisAct.setChecked(False)
636 637 638 639
        event.accept()


if __name__ == '__main__':
640
    from ..dataset import DataSet
641 642
    def run():
        app = QtWidgets.QApplication(sys.argv)
643
        meas = ParticleAnalysis(DataSet("dummydata"))
644
        meas.showMaximized()
645
        return app.exec_()
646 647
    
    run()