mirror of
https://github.com/Athemis/PyDSF.git
synced 2025-04-05 14:46:03 +00:00
Populate instrument list dynamically; Fixes
Populate instrument list dynamically from a given set of instrument objects; Fix a bug when doing mutliple analysis in a row.
This commit is contained in:
parent
b85c58018c
commit
342ed3b3d1
3 changed files with 81 additions and 42 deletions
26
pydsf.py
26
pydsf.py
|
@ -238,7 +238,8 @@ class Well:
|
||||||
|
|
||||||
class Experiment:
|
class Experiment:
|
||||||
|
|
||||||
def __init__(self, exp_type,
|
def __init__(self,
|
||||||
|
instrument,
|
||||||
files=None,
|
files=None,
|
||||||
replicates=None,
|
replicates=None,
|
||||||
t1=25,
|
t1=25,
|
||||||
|
@ -261,7 +262,7 @@ class Experiment:
|
||||||
self.reads = int(round((t2 + 1 - t1) / dt))
|
self.reads = int(round((t2 + 1 - t1) / dt))
|
||||||
self.wellnum = self.cols * self.rows
|
self.wellnum = self.cols * self.rows
|
||||||
self.files = files
|
self.files = files
|
||||||
self.type = exp_type
|
self.instrument = instrument
|
||||||
self.wells = []
|
self.wells = []
|
||||||
self.max_tm = None
|
self.max_tm = None
|
||||||
self.min_tm = None
|
self.min_tm = None
|
||||||
|
@ -290,8 +291,7 @@ class Experiment:
|
||||||
# populate self.plates with data in provided files list
|
# populate self.plates with data in provided files list
|
||||||
i = 1
|
i = 1
|
||||||
for file in files:
|
for file in files:
|
||||||
plate = Plate(plate_type=self.type,
|
plate = Plate(owner=self,
|
||||||
owner=self,
|
|
||||||
filename=file,
|
filename=file,
|
||||||
t1=self.t1,
|
t1=self.t1,
|
||||||
t2=self.t2,
|
t2=self.t2,
|
||||||
|
@ -308,8 +308,7 @@ class Experiment:
|
||||||
# if more than one file is provied, assume that those are replicates
|
# if more than one file is provied, assume that those are replicates
|
||||||
# and add a special plate representing the average results
|
# and add a special plate representing the average results
|
||||||
if len(files) > 1:
|
if len(files) > 1:
|
||||||
self.avg_plate = Plate(plate_type=self.type,
|
self.avg_plate = Plate(owner=self,
|
||||||
owner=self,
|
|
||||||
filename=None,
|
filename=None,
|
||||||
t1=self.t1,
|
t1=self.t1,
|
||||||
t2=self.t2,
|
t2=self.t2,
|
||||||
|
@ -355,7 +354,7 @@ class Experiment:
|
||||||
|
|
||||||
class Plate:
|
class Plate:
|
||||||
|
|
||||||
def __init__(self, plate_type, owner,
|
def __init__(self, owner,
|
||||||
plate_id=None,
|
plate_id=None,
|
||||||
filename=None,
|
filename=None,
|
||||||
replicates=None,
|
replicates=None,
|
||||||
|
@ -387,7 +386,7 @@ class Plate:
|
||||||
self.reads = int(round((t2 + 1 - t1) / dt))
|
self.reads = int(round((t2 + 1 - t1) / dt))
|
||||||
self.wellnum = self.cols * self.rows
|
self.wellnum = self.cols * self.rows
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.type = plate_type
|
self.instrument = owner.instrument
|
||||||
self.wells = []
|
self.wells = []
|
||||||
self.max_tm = None
|
self.max_tm = None
|
||||||
self.min_tm = None
|
self.min_tm = None
|
||||||
|
@ -445,14 +444,9 @@ class Plate:
|
||||||
# If the file is not found, or not accessible: abort
|
# If the file is not found, or not accessible: abort
|
||||||
print('Error accessing file: {}'.format(e))
|
print('Error accessing file: {}'.format(e))
|
||||||
|
|
||||||
if self.type == 'Analytik Jena qTOWER 2.0/2.2':
|
self.wells = self.instrument.loadData(self.filename,
|
||||||
instrument = AnalytikJenaqTower2()
|
self.reads,
|
||||||
|
self.wells)
|
||||||
else:
|
|
||||||
# Raise exception, if the instrument's name is unknown
|
|
||||||
raise NameError('Unknown instrument type: {}'.format(self.type))
|
|
||||||
|
|
||||||
self.wells = instrument.loadData(self.filename, self.reads, self.wells)
|
|
||||||
|
|
||||||
for well in self.wells:
|
for well in self.wells:
|
||||||
well.analyze()
|
well.analyze()
|
||||||
|
|
|
@ -302,7 +302,7 @@ class Ui_MainWindow(object):
|
||||||
MainWindow.setWindowTitle(_translate("MainWindow", "PyDSF"))
|
MainWindow.setWindowTitle(_translate("MainWindow", "PyDSF"))
|
||||||
self.groupBox_experiment.setTitle(_translate("MainWindow", "Experimental Setup"))
|
self.groupBox_experiment.setTitle(_translate("MainWindow", "Experimental Setup"))
|
||||||
self.label_instrument.setText(_translate("MainWindow", "Instrument"))
|
self.label_instrument.setText(_translate("MainWindow", "Instrument"))
|
||||||
self.comboBox_instrument.setItemText(0, _translate("MainWindow", "Analytik Jena qTOWER 2.0/2.2"))
|
#self.comboBox_instrument.setItemText(0, _translate("MainWindow", "Analytik Jena qTOWER 2.0/2.2"))
|
||||||
self.groupBox_data.setToolTip(_translate("MainWindow", "<html><head/><body><p>Add data files to the experiment. If multiple files are loaded, they are treated as replicates.</p></body></html>"))
|
self.groupBox_data.setToolTip(_translate("MainWindow", "<html><head/><body><p>Add data files to the experiment. If multiple files are loaded, they are treated as replicates.</p></body></html>"))
|
||||||
self.groupBox_data.setTitle(_translate("MainWindow", "Data File"))
|
self.groupBox_data.setTitle(_translate("MainWindow", "Data File"))
|
||||||
self.groupBox_replicates.setTitle(_translate("MainWindow", "Replicates"))
|
self.groupBox_replicates.setTitle(_translate("MainWindow", "Replicates"))
|
||||||
|
|
|
@ -12,6 +12,12 @@ from .mplwidget import MplWidget
|
||||||
from pydsf import Experiment, PlotResults
|
from pydsf import Experiment, PlotResults
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
# Import available instruments
|
||||||
|
try:
|
||||||
|
from instruments.analytikJenaqTower2 import AnalytikJenaqTower2
|
||||||
|
except ImportError as err:
|
||||||
|
raise ImportError('Error while loading instrument plugins:', err)
|
||||||
|
|
||||||
VERSION = "1.0"
|
VERSION = "1.0"
|
||||||
_translate = QCoreApplication.translate
|
_translate = QCoreApplication.translate
|
||||||
|
|
||||||
|
@ -25,7 +31,7 @@ class Worker(QRunnable):
|
||||||
finished = pyqtSignal(int)
|
finished = pyqtSignal(int)
|
||||||
|
|
||||||
def __init__(self, owner):
|
def __init__(self, owner):
|
||||||
super(Worker, self).__init__()
|
super().__init__()
|
||||||
self.exp = None
|
self.exp = None
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.signals = WorkerSignals()
|
self.signals = WorkerSignals()
|
||||||
|
@ -35,7 +41,7 @@ class Worker(QRunnable):
|
||||||
c_upper = None
|
c_upper = None
|
||||||
cbar_range = None
|
cbar_range = None
|
||||||
signal_threshold = None
|
signal_threshold = None
|
||||||
instrument_type = self.owner.comboBox_instrument.currentText()
|
#instrument_type = self.owner.comboBox_instrument.currentText()
|
||||||
if self.owner.groupBox_cutoff.isChecked():
|
if self.owner.groupBox_cutoff.isChecked():
|
||||||
c_lower = self.owner.doubleSpinBox_lower.value()
|
c_lower = self.owner.doubleSpinBox_lower.value()
|
||||||
c_upper = self.owner.doubleSpinBox_upper.value()
|
c_upper = self.owner.doubleSpinBox_upper.value()
|
||||||
|
@ -51,7 +57,7 @@ class Worker(QRunnable):
|
||||||
files = []
|
files = []
|
||||||
for item in items:
|
for item in items:
|
||||||
files.append(item.text())
|
files.append(item.text())
|
||||||
self.exp = Experiment(exp_type=instrument_type,
|
self.exp = Experiment(instrument=self.owner.instrument,
|
||||||
files=files,
|
files=files,
|
||||||
t1=self.owner.doubleSpinBox_tmin.value(),
|
t1=self.owner.doubleSpinBox_tmin.value(),
|
||||||
t2=self.owner.doubleSpinBox_tmax.value(),
|
t2=self.owner.doubleSpinBox_tmax.value(),
|
||||||
|
@ -84,6 +90,12 @@ class Tasks(QObject):
|
||||||
def add_task(self, task):
|
def add_task(self, task):
|
||||||
self.tasks.append(task)
|
self.tasks.append(task)
|
||||||
|
|
||||||
|
def remove_task(self, task):
|
||||||
|
self.tasks.remove(task)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.tasks.clear()
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
self.pool.waitForDone()
|
self.pool.waitForDone()
|
||||||
return self.data
|
return self.data
|
||||||
|
@ -95,6 +107,7 @@ class Tasks(QObject):
|
||||||
|
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
self.data.append(task.exp)
|
self.data.append(task.exp)
|
||||||
|
self.remove_task(task)
|
||||||
|
|
||||||
self.signals.finished.emit(self.data)
|
self.signals.finished.emit(self.data)
|
||||||
|
|
||||||
|
@ -120,25 +133,42 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
self.statusBar.addPermanentWidget(self.progressBar)
|
self.statusBar.addPermanentWidget(self.progressBar)
|
||||||
self.statusBar.showMessage(_translate("MainWindow",
|
self.statusBar.showMessage(_translate("MainWindow",
|
||||||
"Welcome to PyDSF"))
|
"Welcome to PyDSF"))
|
||||||
|
|
||||||
self.buttonBox_process.addButton(
|
self.buttonBox_process.addButton(
|
||||||
_translate("MainWindow", "&Start Processing"),
|
_translate("MainWindow", "&Start Processing"),
|
||||||
QDialogButtonBox.AcceptRole)
|
QDialogButtonBox.AcceptRole)
|
||||||
|
|
||||||
self.tasks = Tasks()
|
self.tasks = Tasks()
|
||||||
self.worker = Worker(self)
|
self.tasks.signals.finished.connect(self.on_processing_finished)
|
||||||
|
self.worker = None
|
||||||
self.outputPath = None
|
self.outputPath = None
|
||||||
|
self.instrument = None
|
||||||
|
self.populateInstrumentList()
|
||||||
|
|
||||||
|
def populateInstrumentList(self):
|
||||||
|
self.instruments = [AnalytikJenaqTower2()]
|
||||||
|
for i in range(len(self.instruments)):
|
||||||
|
instrument = self.instruments[i]
|
||||||
|
self.comboBox_instrument.setItemText(i, instrument.name)
|
||||||
|
|
||||||
|
def getInstrumentFromName(self, name):
|
||||||
|
for instrument in self.instruments:
|
||||||
|
if instrument.name == name:
|
||||||
|
return instrument
|
||||||
|
raise IndexError("Requested instrument not")
|
||||||
|
|
||||||
|
def getSelectedInstrument(self):
|
||||||
|
name = str(self.comboBox_instrument.currentText())
|
||||||
|
instrument = self.getInstrumentFromName(name)
|
||||||
|
return instrument
|
||||||
|
|
||||||
@pyqtSlot("QAbstractButton*")
|
@pyqtSlot("QAbstractButton*")
|
||||||
def on_buttonBox_open_reset_clicked(self, button):
|
def on_buttonBox_open_reset_clicked(self, button):
|
||||||
"""
|
"""
|
||||||
Slot documentation goes here.
|
Triggered when either the open or reset button in the data file
|
||||||
|
dialog is clicked. Spawns an open file dialog or resets the listview.
|
||||||
"""
|
"""
|
||||||
if button == self.buttonBox_open_reset.button(QDialogButtonBox.Open):
|
if button == self.buttonBox_open_reset.button(QDialogButtonBox.Open):
|
||||||
filenames = QFileDialog.getOpenFileNames(
|
filenames = QFileDialog.getOpenFileNames(
|
||||||
self,
|
self, _translate("MainWindow", "Open data file"), '',
|
||||||
_translate("MainWindow", "Open data file"), '',
|
|
||||||
_translate("MainWindow", "Text files (*.txt *.csv)"))
|
_translate("MainWindow", "Text files (*.txt *.csv)"))
|
||||||
self.listWidget_data.addItems(filenames[0])
|
self.listWidget_data.addItems(filenames[0])
|
||||||
if self.listWidget_data.count() > 1:
|
if self.listWidget_data.count() > 1:
|
||||||
|
@ -151,19 +181,23 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
@pyqtSlot("QAbstractButton*")
|
@pyqtSlot("QAbstractButton*")
|
||||||
def on_buttonBox_output_clicked(self, button):
|
def on_buttonBox_output_clicked(self, button):
|
||||||
if button == self.buttonBox_output.button(QDialogButtonBox.Open):
|
if button == self.buttonBox_output.button(QDialogButtonBox.Open):
|
||||||
path = QFileDialog.getExistingDirectory(parent=self, caption=_translate(
|
caption = _translate('MainWindow', 'Choose output path')
|
||||||
'MainWindow', 'Choose output path'), options=QFileDialog.ShowDirsOnly)
|
path = QFileDialog.getExistingDirectory(
|
||||||
|
parent=self,
|
||||||
|
caption=caption,
|
||||||
|
options=QFileDialog.ShowDirsOnly)
|
||||||
self.lineEdit_output.setText(path.strip())
|
self.lineEdit_output.setText(path.strip())
|
||||||
|
|
||||||
@pyqtSlot("QString")
|
@pyqtSlot("QString")
|
||||||
def on_comboBox_instrument_currentIndexChanged(self, p0):
|
def on_comboBox_instrument_currentIndexChanged(self, p0):
|
||||||
"""
|
"""
|
||||||
Slot documentation goes here.
|
Triggered when another instrument is selected from the combobox.
|
||||||
"""
|
"""
|
||||||
if p0 == 'Analytik Jena qTOWER 2.0/2.2':
|
self.instrument = self.getInstrumentFromName(p0)
|
||||||
self.groupBox_temp.setEnabled(True)
|
if self.instrument.providesTempRange:
|
||||||
else:
|
|
||||||
self.groupBox_temp.setEnabled(False)
|
self.groupBox_temp.setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.groupBox_temp.setEnabled(True)
|
||||||
|
|
||||||
def generate_plot_tab(self, name):
|
def generate_plot_tab(self, name):
|
||||||
tab = MplWidget(parent=self.tabWidget)
|
tab = MplWidget(parent=self.tabWidget)
|
||||||
|
@ -221,19 +255,23 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
Slot documentation goes here.
|
Slot documentation goes here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.instrument = self.getSelectedInstrument()
|
||||||
|
|
||||||
if self.listWidget_data.count() < 1:
|
if self.listWidget_data.count() < 1:
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self, _translate("MainWindow", "Error"),
|
self, _translate("MainWindow", "Error"),
|
||||||
_translate("MainWindow", "No data file loaded!"),
|
_translate("MainWindow", "No data file loaded!"),
|
||||||
QMessageBox.Close, QMessageBox.Close)
|
QMessageBox.Close, QMessageBox.Close)
|
||||||
return
|
return
|
||||||
if self.groupBox_output.isChecked() and self.lineEdit_output.text().strip() == '':
|
if (self.groupBox_output.isChecked() and
|
||||||
|
self.lineEdit_output.text().strip() == ''):
|
||||||
QMessageBox.critical(
|
QMessageBox.critical(
|
||||||
self, _translate("MainWindow", "Error"),
|
self, _translate("MainWindow", "Error"),
|
||||||
_translate("MainWindow", "No output path set!"),
|
_translate("MainWindow", "No output path set!"),
|
||||||
QMessageBox.Close, QMessageBox.Close)
|
QMessageBox.Close, QMessageBox.Close)
|
||||||
return
|
return
|
||||||
elif self.groupBox_output.isChecked() and self.lineEdit_output.text().strip() != '':
|
elif (self.groupBox_output.isChecked() and
|
||||||
|
self.lineEdit_output.text().strip() != ''):
|
||||||
path = self.lineEdit_output.text().strip()
|
path = self.lineEdit_output.text().strip()
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
self.outputPath = self.lineEdit_output.text().strip()
|
self.outputPath = self.lineEdit_output.text().strip()
|
||||||
|
@ -246,8 +284,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
if self.spinBox_signal_threshold.value(
|
if self.spinBox_signal_threshold.value(
|
||||||
) == 0 and self.groupBox_signal_threshold.isChecked():
|
) == 0 and self.groupBox_signal_threshold.isChecked():
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
self, _translate("MainWindow", "Warning"),
|
self, _translate("MainWindow", "Warning"), _translate(
|
||||||
_translate(
|
|
||||||
"MainWindow",
|
"MainWindow",
|
||||||
"Signal threshold is currently set to zero."),
|
"Signal threshold is currently set to zero."),
|
||||||
QMessageBox.Ok, QMessageBox.Ok)
|
QMessageBox.Ok, QMessageBox.Ok)
|
||||||
|
@ -256,12 +293,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
self.progressBar.setEnabled(True)
|
self.progressBar.setEnabled(True)
|
||||||
self.statusBar.showMessage(_translate("MainWindow", "Processing..."))
|
self.statusBar.showMessage(_translate("MainWindow", "Processing..."))
|
||||||
|
|
||||||
self.tasks.signals.finished.connect(self.on_processing_finished)
|
self.worker = Worker(self)
|
||||||
self.tasks.add_task(self.worker)
|
self.tasks.add_task(self.worker)
|
||||||
self.tasks.start()
|
self.tasks.start()
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_processing_finished(self):
|
def on_processing_finished(self):
|
||||||
|
# Clear all jobs from task list
|
||||||
|
# self.tasks.clear()
|
||||||
# For now, only read the first entry
|
# For now, only read the first entry
|
||||||
exp = self.tasks.data[0]
|
exp = self.tasks.data[0]
|
||||||
|
|
||||||
|
@ -279,19 +318,25 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
if self.checkBox_savetables.isChecked():
|
if self.checkBox_savetables.isChecked():
|
||||||
for plate in exp.plates:
|
for plate in exp.plates:
|
||||||
plate.write_tm_table(
|
plate.write_tm_table(
|
||||||
'{}/plate_{}_tm.csv'.format(self.outputPath, str(plate.id)))
|
'{}/plate_{}_tm.csv'.format(self.outputPath,
|
||||||
|
str(plate.id)))
|
||||||
plate.write_data_table(
|
plate.write_data_table(
|
||||||
'{}/plate_{}_dI_dT.csv'.format(self.outputPath, str(plate.id)), dataType='derivative')
|
'{}/plate_{}_dI_dT.csv'.format(self.outputPath,
|
||||||
|
str(plate.id)),
|
||||||
|
dataType='derivative')
|
||||||
plate.write_data_table(
|
plate.write_data_table(
|
||||||
'{}/plate_{}_filtered_data.csv'.format(self.outputPath,
|
'{}/plate_{}_filtered_data.csv'.format(self.outputPath,
|
||||||
str(plate.id)), dataType='filtered')
|
str(plate.id)),
|
||||||
|
dataType='filtered')
|
||||||
plate.write_data_table('{}/plate_{}_raw_data.csv'.format(
|
plate.write_data_table('{}/plate_{}_raw_data.csv'.format(
|
||||||
self.outputPath, str(plate.id)))
|
self.outputPath, str(plate.id)))
|
||||||
|
|
||||||
if exp.avg_plate:
|
if exp.avg_plate:
|
||||||
exp.avg_plate.write_tm_table(
|
exp.avg_plate.write_tm_table(
|
||||||
'{}/plate_{}_tm_avg.csv'.format(
|
'{}/plate_{}_tm_avg.csv'.format(
|
||||||
self.outputPath, str(self.worker.exp.avg_plate.id)), avg=True)
|
self.outputPath,
|
||||||
|
str(self.worker.exp.avg_plate.id)),
|
||||||
|
avg=True)
|
||||||
|
|
||||||
self.progressBar.setEnabled(False)
|
self.progressBar.setEnabled(False)
|
||||||
self.statusBar.showMessage(_translate("MainWindow", "Finished!"))
|
self.statusBar.showMessage(_translate("MainWindow", "Finished!"))
|
||||||
|
|
Loading…
Add table
Reference in a new issue