From cf75c8ee099d509c9de94d6225f07103333a8c31 Mon Sep 17 00:00:00 2001 From: Alexander Minges Date: Fri, 30 Jan 2015 20:14:56 +0100 Subject: [PATCH] Embedded plots into GUI --- .gitignore | 2 +- pydsf.py | 583 +++++++++++++++++++++----------------------- ui/Ui_mainwindow.py | 57 +++-- ui/icons.qrc | 4 +- ui/mainwindow.py | 64 +++-- ui/mainwindow.ui | 60 ++++- ui/mplwidget.py | 25 ++ 7 files changed, 446 insertions(+), 349 deletions(-) create mode 100644 ui/mplwidget.py diff --git a/.gitignore b/.gitignore index 1532d61..a36fb84 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ var/ *.egg-info/ .installed.cfg *.egg -.idea +.idea/ # PyInstaller # Usually these files are written by a python script from a template diff --git a/pydsf.py b/pydsf.py index b3d4e4c..65c3310 100644 --- a/pydsf.py +++ b/pydsf.py @@ -1,6 +1,7 @@ #! /usr/bin/env python2 # -*- coding: utf-8 -*- import csv +import multiprocessing as mp try: import matplotlib as mpl @@ -23,54 +24,56 @@ except ImportError: raise ImportError('----- NumPy must be installed. -----') try: - from scipy.signal import filtfilt, butter + from scipy.signal import filtfilt, butter from scipy import interpolate from scipy import optimize except ImportError: raise ImportError('----- SciPy must be installed. -----') + class Well: - def __init__(self, owner): + def __init__(self, owner): self.owner = owner self.name = None - self.raw = np.zeros(self.owner.reads, dtype=np.float) - self.filtered = np.zeros(self.owner.reads, dtype=np.float) - self.derivatives = np.zeros((4, self.owner.reads)) - self.splines = {"raw": None, - "filtered": None, - "derivative1": None} + self.raw = np.zeros(self.owner.reads, dtype=np.float) + self.filtered = np.zeros(self.owner.reads, dtype=np.float) + self.derivatives = np.zeros((4, self.owner.reads)) + self.splines = {"raw": None, + "filtered": None, + "derivative1": None} self.tm = np.NaN self.tm_sd = np.NaN self.baseline_correction = owner.baseline_correction self.baseline = None - + def filter_raw(self): """ Apply a filter to the raw data """ b, a = butter(3, 0.3) self.filtered = filtfilt(b, a, self.raw) - + def calc_spline(self, y): """ Calculate a spline that represents the smoothed data points """ spline = interpolate.InterpolatedUnivariateSpline(self.owner.temprange, y) - return spline - + return spline + def calc_derivatives(self, spline='filtered'): for t in self.owner.temprange: temp = self.splines[spline].derivatives(t) for i in range(4): - self.derivatives[i, t - self.owner.t1] = temp[i] + self.derivatives[i, t - self.owner.t1] = temp[i] - def calc_baseline(self, y): + @staticmethod + def calc_baseline(y): try: baseline = peakutils.baseline(y) return baseline except: return np.NaN - + def calc_tm(self): """ Calculate the Tm of the well. Returns either the Tm or 'np.NaN'. @@ -83,7 +86,7 @@ class Well: self.owner.denatured_wells.append(self) if self.owner.tm_cutoff_low != self.owner.t1 or self.owner.tm_cutoff_high != self.owner.t1: - x = np.arange(self.owner.tm_cutoff_low, self.owner.tm_cutoff_high + 1, self.owner.dt, dtype=float) + x = np.arange(self.owner.tm_cutoff_low, self.owner.tm_cutoff_high + 1, self.owner.dt, dtype=float) x = self.owner.temprange y = self.derivatives[1] @@ -93,83 +96,85 @@ class Well: try: peak_indexes = peakutils.indexes(y, min_dist=len(x)) # calculate a rough estimate of peaks; set min_dist - # temperature range to only find one/the highest peak - tm = peakutils.interpolate(x, y, ind=peak_indexes)[0] # increase resolution by fitting gaussian function - # to peak + # temperature range to only find one/the highest peak + tm = peakutils.interpolate(x, y, ind=peak_indexes)[0] # increase resolution by fitting gaussian function + # to peak except: return np.NaN # In case of error, return no peak - + try: - # Check if the peak is within cutoff range - if tm and tm >= self.owner.tm_cutoff_low and tm <= self.owner.tm_cutoff_high: - self.owner.denatured_wells.remove(self) # If everything is fine, remove the denatured flag - return tm # and return the Tm - else: - return np.NaN # otherwise, return NaN + # Check if the peak is within cutoff range + if tm and tm >= self.owner.tm_cutoff_low and tm <= self.owner.tm_cutoff_high: + self.owner.denatured_wells.remove(self) # If everything is fine, remove the denatured flag + return tm # and return the Tm + else: + return np.NaN # otherwise, return NaN except: return np.NaN # In case of error, return NaN - + def is_denatured(self): """ Check if the well is denatured. Returns true if the well has been already flagged as denatured, no Tm was found, or if the initial signal intensity is above a user definded threshold. """ - denatured = True # Assumption is that the well is denatured - - if self in self.owner.denatured_wells: # check if the well is already flagged as denatured - return denatured # return true if it is - + denatured = True # Assumption is that the well is denatured + + if self in self.owner.denatured_wells: # check if the well is already flagged as denatured + return denatured # return true if it is + if self.tm and (self.tm <= self.owner.tm_cutoff_low or self.tm >= self.owner.tm_cutoff_high): denatured = True return denatured - - for i in self.derivatives[1]: # Iterate over all points in the first derivative - if i > 0: # If a positive slope is found - denatured = False # set denatured flag to False - - reads = int(round(self.owner.reads/10)) # How many values should be checked against the signal threshold: - # 1/10 of the total number of data point - read = 0 # Initialize running variable representing the current data point - + + for i in self.derivatives[1]: # Iterate over all points in the first derivative + if i > 0: # If a positive slope is found + denatured = False # set denatured flag to False + + reads = int(round(self.owner.reads / 10)) # How many values should be checked against the signal threshold: + # 1/10 of the total number of data point + read = 0 # Initialize running variable representing the current data point + if not denatured: - for j in self.filtered: # Iterate over the filtered data - if self.owner.signal_threshold: # If a signal threshold was defined - if j > self.owner.signal_threshold and read <= reads: # iterate over 1/10 of all data points - # and check for values larger than the threshold. - denatured = True # Set flag to True if a match is found + for j in self.filtered: # Iterate over the filtered data + if self.owner.signal_threshold: # If a signal threshold was defined + if j > self.owner.signal_threshold and read <= reads: # iterate over 1/10 of all data points + # and check for values larger than the threshold. + denatured = True # Set flag to True if a match is found print("{}: {}".format(self.name, j)) - return denatured # and return + return denatured # and return read += 1 - + return denatured - + def analyze(self): self.filter_raw() self.splines["raw"] = self.calc_spline(self.raw) self.splines["filtered"] = self.calc_spline(self.filtered) - + self.calc_derivatives() if self.baseline_correction: self.baseline = self.calc_baseline(self.derivatives[1]) if self.is_denatured(): self.owner.denatured_wells.append(self) - + self.splines["derivative1"] = self.calc_spline(self.derivatives[1]) self.tm = self.calc_tm() if self.tm is None: self.tm = np.NaN - + + class Experiment: - def __init__(self, type, gui=None, files=None, replicates=None, t1=25, t2=95, dt=1, cols=12, rows=8, cutoff_low=None, cutoff_high=None, signal_threshold=None, color_range=None, baseline_correction=False): + def __init__(self, type, gui=None, files=None, replicates=None, t1=25, t2=95, dt=1, cols=12, rows=8, + cutoff_low=None, cutoff_high=None, signal_threshold=None, color_range=None, baseline_correction=False): self.replicates = replicates self.cols = cols self.rows = rows self.t1 = t1 self.t2 = t2 self.dt = dt - self.temprange = np.arange(self.t1, self.t2 + 1, self.dt, dtype=float) + self.temprange = np.arange(self.t1, self.t2 + 1, self.dt, dtype=float) self.reads = int(round((t2 + 1 - t1) / dt)) self.wellnum = self.cols * self.rows self.files = files @@ -178,10 +183,10 @@ class Experiment: self.max_tm = None self.min_tm = None self.replicates = None - self.gui=gui + self.gui = gui self.signal_threshold = signal_threshold self.avg_plate = None - self.baseline_correction=baseline_correction + self.baseline_correction = baseline_correction if cutoff_low: self.tm_cutoff_low = cutoff_low else: @@ -193,48 +198,54 @@ class Experiment: if color_range: self.color_range = color_range else: - self.color_range = (self.t1, self.t2) - + self.color_range = None + self.plates = [] - + i = 1 for file in files: - plate = Plate(type=self.type, owner=self, filename=file, t1=self.t1, t2=self.t2, dt=self.dt, cols=self.cols, rows=self.rows, cutoff_low=self.tm_cutoff_low, cutoff_high=self.tm_cutoff_high, signal_threshold=self.signal_threshold, color_range=self.color_range) + plate = Plate(type=self.type, owner=self, filename=file, t1=self.t1, t2=self.t2, dt=self.dt, cols=self.cols, + rows=self.rows, cutoff_low=self.tm_cutoff_low, cutoff_high=self.tm_cutoff_high, + signal_threshold=self.signal_threshold, color_range=self.color_range) plate.id = i self.plates.append(plate) i += 1 if len(files) > 1: - self.avg_plate = Plate(type=self.type, owner=self, filename=None, t1=self.t1, t2=self.t2, dt=self.dt, cols=self.cols, rows=self.rows, cutoff_low=self.tm_cutoff_low, cutoff_high=self.tm_cutoff_high, signal_threshold=self.signal_threshold, color_range=self.color_range) + self.avg_plate = Plate(type=self.type, owner=self, filename=None, t1=self.t1, t2=self.t2, dt=self.dt, + cols=self.cols, rows=self.rows, cutoff_low=self.tm_cutoff_low, + cutoff_high=self.tm_cutoff_high, signal_threshold=self.signal_threshold, + color_range=self.color_range) self.avg_plate.id = 'average' - + def analyze(self): for plate in self.plates: plate.analyze(gui=self.gui) - + if len(self.plates) > 1: - - #self.tm_replicates = np.zeros( self.wellnum, dtype=float ) - #self.tm_replicates_sd = np.zeros( self.wellnum, dtype=float ) - - + + # self.tm_replicates = np.zeros( self.wellnum, dtype=float ) + # self.tm_replicates_sd = np.zeros( self.wellnum, dtype=float ) + + for i in range(self.wellnum): - tmp = [] + tmp = [] for plate in self.plates: tm = plate.wells[i].tm self.avg_plate.wells[i].name = plate.wells[i].name if plate.wells[i] not in plate.denatured_wells: tmp.append(tm) if len(tmp) > 0: - #self.avg_plate.wells[i].tm = (sum(tmp)/len(tmp)) + # self.avg_plate.wells[i].tm = (sum(tmp)/len(tmp)) self.avg_plate.wells[i].tm = np.mean(tmp) self.avg_plate.wells[i].tm_sd = np.std(tmp) - #self.tm_replicates[i] = (sum(tmp)/len(tmp)) + # self.tm_replicates[i] = (sum(tmp)/len(tmp)) else: self.avg_plate.denatured_wells.append(self.avg_plate.wells[i]) class Plate: - def __init__(self, type, owner, id=None, filename=None, replicates=None, t1=None, t2=None, dt=None, cols=12, rows=8, cutoff_low=None, cutoff_high=None, signal_threshold=None, color_range=None): + def __init__(self, type, owner, id=None, filename=None, replicates=None, t1=None, t2=None, dt=None, cols=12, rows=8, + cutoff_low=None, cutoff_high=None, signal_threshold=None, color_range=None): self.cols = cols self.rows = rows self.owner = owner @@ -250,7 +261,7 @@ class Plate: self.dt = dt else: self.dt = owner.dt - self.temprange = np.arange(self.t1, self.t2 + 1, self.dt, dtype=float) + self.temprange = np.arange(self.t1, self.t2 + 1, self.dt, dtype=float) self.reads = int(round((t2 + 1 - t1) / dt)) self.wellnum = self.cols * self.rows self.filename = filename @@ -274,28 +285,26 @@ class Plate: self.color_range = color_range else: self.color_range = None - + self.denatured_wells = [] self.tms = [] - - for i in range(self.wellnum): - well = Well(owner = self) - self.wells.append(well) - - #self.analyze() - + for i in range(self.wellnum): + well = Well(owner=self) + self.wells.append(well) + + def analytikJena(self): """ Data processing for Analytik Jena qTower 2.0 export files """ - with open(self.filename, 'r') as f: - reader = csv.reader(f, delimiter=';', quoting=csv.QUOTE_NONE) - + with open(self.filename, 'r') as f: + reader = csv.reader(f, delimiter=';', quoting=csv.QUOTE_NONE) + i = 0 for row in reader: - temp = np.zeros(self.reads, dtype=float) - for read in range(self.reads+1): + temp = np.zeros(self.reads, dtype=float) + for read in range(self.reads + 1): if read > 0: try: temp[read - 1] = row[read] @@ -306,15 +315,15 @@ class Plate: self.wells[i].raw = temp i += 1 - def analyze(self, gui=None): + def analyze(self, gui=None): try: # Try to access data file in the given path - with open(self.filename) as f: pass + with open(self.filename) as f: + pass except IOError as e: # If the file is not found, or not accessible: abort print('Error accessing file: {}'.format(e)) - - + if self.type == 'Analytik Jena qTOWER 2.0/2.2': self.analytikJena() if gui: @@ -322,23 +331,23 @@ class Plate: else: # Raise exception, if the instrument's name is unknown raise NameError('Unknown instrument type: {}'.format(self.type)) - + for well in self.wells: well.analyze() if gui: update_progress_bar(gui.pb, 15) - + self.tms.append(well.tm) - + if self.replicates: if self.replicates == 'rows': print("rows") if self.replicates == 'cols': print("cols") - #print(self.tms) + # print(self.tms) self.max_tm = max(self.tms) self.min_tm = min(self.tms) - + def write_tm_table(self, filename): with open(filename, 'w') as f: f.write('#{:<4s}{:>13s}\n'.format('ID', '"Tm [°C]"')) @@ -347,7 +356,7 @@ class Plate: f.write('{:<5s}{:>12s}\n'.format(well.name, 'NaN')) else: f.write('{:<5s}{:>12s}\n'.format(well.name, str(well.tm))) - + def write_avg_tm_table(self, filename): with open(filename, 'w') as f: f.write('#{:<4s}{:>13s}{:>13s}\n'.format('"ID"', '"Tm [°C]"', '"SD"')) @@ -356,7 +365,7 @@ class Plate: f.write('{:<5s}{:>12s}{:>12s}\n'.format(well.name, 'NaN', 'NaN')) else: f.write('{:<5s}{:>12s}{:>12s}\n'.format(well.name, str(well.tm), str(well.tm_sd))) - + def write_raw_table(self, filename): with open(filename, 'w') as f: f.write('#"Raw data"\n') @@ -364,16 +373,16 @@ class Plate: for well in self.wells: f.write('{:>15s}'.format(well.name)) f.write('\n') - + i = 0 for t in self.temprange: f.write('{:<10s}'.format(str(t))) for well in self.wells: d = well.raw[i] - f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) + f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) f.write('\n') i += 1 - + def write_filtered_table(self, filename): with open(filename, 'w') as f: f.write('#"Filtered data" \n') @@ -381,16 +390,16 @@ class Plate: for well in self.wells: f.write('{:>15s}'.format(well.name)) f.write('\n') - + i = 0 for t in self.temprange: f.write('{:<10s}'.format(str(t))) for well in self.wells: d = well.filtered[i] - f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) + f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) f.write('\n') i += 1 - + def write_derivative_table(self, filename): with open(filename, 'w') as f: f.write('#"Derivative dI/dT"\n') @@ -398,13 +407,13 @@ class Plate: for well in self.wells: f.write('{:>15s}'.format(well.name)) f.write('\n') - + i = 0 for t in self.temprange: f.write('{:<10s}'.format(str(t))) for well in self.wells: d = well.derivatives[1][i] - f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) + f.write('{:>-15.3f}'.format(float(np.round(d, decimals=3)))) f.write('\n') i += 1 @@ -412,209 +421,185 @@ class Plate: def write_baseline_corrected_table(self, filename): raise NotImplementedError - - + + def update_progress_bar(bar, value): bar.setValue(value) - -def plot_tm_heatmap_average(experiment, gui=None): - """ - Plot Tm heatmap (Fig. 1) - """ - x = 1 # Position in columns - y = 1 # Position in rows - x_values = [] # Array holding the columns - y_values = [] # Array holding the rows - c_values = [] # Array holding the color values aka Tm -# c = well.tm # If not, set color to Tm -# if c < experiment.tm_cutoff_low: # Check if Tm is lower that the cutoff -# c = experiment.tm_cutoff_low # If it is, set color to cutoff -# elif c > experiment.tm_cutoff_high: # Check if Tm is higher that the cutoff -# c = experiment.tm_cutoff_high # If it is, set color to cutoff -# else: # If the plate is denatured -# c = experiment.tm_cutoff_low # Set its color to the low cutoff - for c in experiment.tm_replicates: +class PlotResults(): - x_values.append(x) # Add values to the respective arrays - y_values.append(y) - c_values.append(c) - x += 1 # Increase column by one - if x > experiment.cols: # If maximum column per row is reached - x = 1 # reset column to one - y += 1 # and increase row by one + def __init__(self, experiment): + self.experiment = experiment - fig1 = plt.figure() # new figure - ax1 = fig1.add_subplot(1, 1, 1) # A single canvas - ax1.autoscale(tight=True) # Scale plate size - ax1.xaxis.set_major_locator(ticker.MaxNLocator(experiment.cols + 1)) # n columns - ax1.yaxis.set_major_locator(ticker.MaxNLocator(experiment.rows + 1)) # n rows - if experiment.color_range: - cax = ax1.scatter(x_values, y_values, s=300, c=c_values, marker='s', vmin=experiment.color_range[0], vmax=experiment.color_range[1]) # plot wells and color using the colormap - else: - cax = ax1.scatter(x_values, y_values, s=300, c=c_values, marker='s') # plot wells and color using the colormap - ax1.invert_yaxis() # invert y axis to math plate layout - cbar = fig1.colorbar(cax) # show colorbar - ax1.set_xlabel('Columns') # set axis and colorbar label - ax1.set_ylabel('Rows') - ax1.set_title('$T_m$ heatmap (average)') - cbar.set_label(u"Temperature [°C]") - - -def plot_tm_heatmap_single(plate, gui=None): - """ - Plot Tm heatmap (Fig. 1) - """ - x = 1 # Position in columns - y = 1 # Position in rows - x_values = [] # Array holding the columns - y_values = [] # Array holding the rows - c_values = [] # Array holding the color values aka Tm - dx_values = [] - dy_values = [] - dc_values = [] - for well in plate.wells: # Iterate over all wells - if well not in plate.denatured_wells: # Check if well is denatured (no Tm found) - c = well.tm # If not, set color to Tm - if c < plate.tm_cutoff_low: # Check if Tm is lower that the cutoff - c = plate.tm_cutoff_low # If it is, set color to cutoff - elif c > plate.tm_cutoff_high: # Check if Tm is higher that the cutoff - c = plate.tm_cutoff_high # If it is, set color to cutoff - else: # If the plate is denatured - c = plate.tm_cutoff_low # Set its color to the low cutoff - dx_values.append(x) - dy_values.append(y) - x_values.append(x) # Add values to the respective arrays - y_values.append(y) - c_values.append(c) - x += 1 # Increase column by one - if x > plate.cols: # If maximum column per row is reached - x = 1 # reset column to one - y += 1 # and increase row by one + def plot_tm_heatmap_single(self, plate, widget): + """ + Plot Tm heatmap (Fig. 1) + """ + x = 1 # Position in columns + y = 1 # Position in rows + x_values = [] # Array holding the columns + y_values = [] # Array holding the rows + c_values = [] # Array holding the color values aka Tm + dx_values = [] + dy_values = [] + dc_values = [] + canvas = widget.canvas + canvas.clear() + for well in plate.wells: # Iterate over all wells + if well not in plate.denatured_wells: # Check if well is denatured (no Tm found) + c = well.tm # If not, set color to Tm + if c < plate.tm_cutoff_low: # Check if Tm is lower that the cutoff + c = plate.tm_cutoff_low # If it is, set color to cutoff + elif c > plate.tm_cutoff_high: # Check if Tm is higher that the cutoff + c = plate.tm_cutoff_high # If it is, set color to cutoff + else: # If the plate is denatured + c = plate.tm_cutoff_low # Set its color to the low cutoff + dx_values.append(x) + dy_values.append(y) + x_values.append(x) # Add values to the respective arrays + y_values.append(y) + c_values.append(c) + x += 1 # Increase column by one + if x > plate.cols: # If maximum column per row is reached + x = 1 # reset column to one + y += 1 # and increase row by one - fig1 = plt.figure() # new figure - ax1 = fig1.add_subplot(1, 1, 1) # A single canvas - ax1.autoscale(tight=True) # Scale plate size - ax1.xaxis.set_major_locator(ticker.MaxNLocator(plate.cols + 1)) # n columns - ax1.yaxis.set_major_locator(ticker.MaxNLocator(plate.rows + 1)) # n rows - if plate.color_range: - cax = ax1.scatter(x_values, y_values, s=305, c=c_values, marker='s', vmin=plate.color_range[0], vmax=plate.color_range[1]) # plot wells and color using the colormap - else: - cax = ax1.scatter(x_values, y_values, s=305, c=c_values, marker='s') # plot wells and color using the colormap - - cax2 = ax1.scatter(dx_values, dy_values, s=80, c='white', marker='x', linewidths=(1.5,)) - ax1.invert_yaxis() # invert y axis to math plate layout - cbar = fig1.colorbar(cax) # show colorbar - ax1.set_xlabel('Columns') # set axis and colorbar label - ax1.set_ylabel('Rows') - - if str(plate.id) == 'average': - title = '$T_m$ heatmap (average)' - else: - title = '$T_m$ heatmap (plate #{})'.format(str(plate.id)) - ax1.set_title(title) - cbar.set_label(u"Temperature [°C]") - - #magenta_patch = mpatches.Patch(color='magenta', label='Denatured') - #fig1.legend([magenta_patch], 'Denatured', loc='lower right', bbox_to_anchor=[0.5, 0.5]) - -# if gui: -# update_progress_bar(gui.pb, 50) - -def plot_derivative(plate, gui=None): - """ - Plot derivatives (Fig. 2) - """ - fig2 = plt.figure() # new figure - fig2.suptitle('Individual Derivatives (plate #{})'.format(str(plate.id))) # set title - - for plot_num in range(1, plate.wellnum + 1): # iterate over all wells - well = plate.wells[plot_num - 1] # get single well based on current plot number - ax = fig2.add_subplot(plate.rows, plate.cols, plot_num) # add new subplot - ax.autoscale(tight=True) # scale to data - ax.set_title(well.name, size='xx-small') # set title of current subplot to well identifier - - if well in plate.denatured_wells: - ax.patch.set_facecolor('#FFD6D6') - - if plot_num == plate.wellnum - plate.cols + 1: # add axis label to the subplot in the bottom left corner of the figure - ax.set_xlabel(u'T [°C]', size='xx-small') - ax.set_ylabel('dI/dT', size='xx-small') - - x = plate.temprange # set values for the x axis to the given temperature range - if well.baseline_correction: - print(well.baseline) - y = well.derivatives[1] - well.baseline + fig1 = canvas.fig # new figure + ax1 = fig1.add_subplot(1, 1, 1) # A single canvas + ax1.autoscale(tight=True) # Scale plate size + ax1.xaxis.set_major_locator(ticker.MaxNLocator(plate.cols + 1)) # n columns + ax1.yaxis.set_major_locator(ticker.MaxNLocator(plate.rows + 1)) # n rows + if plate.color_range: + cax = ax1.scatter(x_values, y_values, s=305, c=c_values, marker='s', vmin=plate.color_range[0], + vmax=plate.color_range[1]) # plot wells and color using the colormap else: - y = well.derivatives[1] # grab y values from the first derivative of the well - - ax.xaxis.set_major_locator(ticker.MaxNLocator(4)) # only show three tickmarks on both axes - ax.yaxis.set_major_locator(ticker.MaxNLocator(4)) - if well not in plate.denatured_wells: # check if well is denatured (without determined Tm) - tm = well.tm # if not, grab its Tm + cax = ax1.scatter(x_values, y_values, s=305, c=c_values, marker='s') # plot wells and color using the colormap + + cax2 = ax1.scatter(dx_values, dy_values, s=80, c='white', marker='x', linewidths=(1.5,)) + ax1.invert_yaxis() # invert y axis to math plate layout + cbar = fig1.colorbar(cax) # show colorbar + ax1.set_xlabel('Columns') # set axis and colorbar label + ax1.set_ylabel('Rows') + + if str(plate.id) == 'average': + title = '$T_m$ heatmap (average)' else: - tm = np.NaN # else set Tm to np.NaN - if tm: - ax.axvline(x=tm) # plot vertical line at the Tm - ax.axvspan(plate.t1, plate.tm_cutoff_low, facecolor='0.8', alpha=0.5) # shade lower cutoff area - ax.axvspan(plate.tm_cutoff_high, plate.t2, facecolor='0.8', alpha=0.5) # shade higher cutoff area - for label in ax.get_xticklabels() + ax.get_yticklabels(): # set fontsize for all tick labels to xx-small - label.set_fontsize('xx-small') - - cax = ax.plot(x, y) # plot data to the current subplot -# if gui: -# update_progress_bar(gui.pb, 75) - -def plot_raw(plate, gui=None): - """ - Plot raw data (Fig. 3) - """ - fig3 = plt.figure() # new figure - fig3.suptitle('Raw Data (plate #{})'.format(str(plate.id))) # set title - - for plot_num in range(1, plate.wellnum + 1): # iterate over all wells - well = plate.wells[plot_num - 1] # get single well based on current plot number - ax = fig3.add_subplot(plate.rows, plate.cols, plot_num) # add new subplot - ax.autoscale(tight=True) # scale to data - ax.set_title(well.name, size='xx-small') # set title of current subplot to well identifier - - if well in plate.denatured_wells: - ax.patch.set_facecolor('#FFD6D6') - - if plot_num == plate.wellnum - plate.cols + 1: # add axis label to the subplot in the bottom left corner of the figure - ax.set_xlabel(u'T [°C]', size='xx-small') - ax.set_ylabel('I', size='xx-small') - - x = plate.temprange # set values for the x axis to the given temperature range - y = well.raw # grab y values from the raw data of the well - - ax.xaxis.set_major_locator(ticker.MaxNLocator(4)) # only show three tickmarks on both axes - ax.yaxis.set_major_locator(ticker.MaxNLocator(4)) - ax.axvspan(plate.t1, plate.tm_cutoff_low, facecolor='0.8', alpha=0.5) # shade lower cutoff area - ax.axvspan(plate.tm_cutoff_high, plate.t2, facecolor='0.8', alpha=0.5) # shade higher cutoff area - for label in ax.get_xticklabels() + ax.get_yticklabels(): # set fontsize for all tick labels to xx-small - label.set_fontsize('xx-small') - - cax = ax.plot(x, y) # plot data to the current subplot - -# if gui: -# update_progress_bar(gui.pb, 100) -# gui.pb.hide() + title = '$T_m$ heatmap (plate #{})'.format(str(plate.id)) + ax1.set_title(title) + cbar.set_label(u"Temperature [°C]") -def plot(experiment, gui=None): - for plate in experiment.plates: - plot_raw(plate) - plot_derivative(plate) - plot_tm_heatmap_single(plate) - - if len(experiment.plates) > 1: - plot_tm_heatmap_single(experiment.avg_plate) - #plot_tm_heatmap_average(experiment) - - plt.show() - + canvas.draw() + + def plot_derivative(self, plate, widget): + """ + Plot derivatives (Fig. 2) + """ + canvas = widget.canvas + canvas.clear() + fig2 = canvas.fig # new figure + fig2.suptitle('Individual Derivatives (plate #{})'.format(str(plate.id))) # set title + + for plot_num in range(1, plate.wellnum + 1): # iterate over all wells + well = plate.wells[plot_num - 1] # get single well based on current plot number + ax = fig2.add_subplot(plate.rows, plate.cols, plot_num) # add new subplot + ax.autoscale(tight=True) # scale to data + ax.set_title(well.name, size='xx-small') # set title of current subplot to well identifier + + if well in plate.denatured_wells: + ax.patch.set_facecolor('#FFD6D6') + + if plot_num == plate.wellnum - plate.cols + 1: # add axis label to the subplot in the bottom left corner of the figure + ax.set_xlabel(u'T [°C]', size='xx-small') + ax.set_ylabel('dI/dT', size='xx-small') + + x = plate.temprange # set values for the x axis to the given temperature range + if well.baseline_correction: + print(well.baseline) + y = well.derivatives[1] - well.baseline + else: + y = well.derivatives[1] # grab y values from the first derivative of the well + + ax.xaxis.set_major_locator(ticker.MaxNLocator(4)) # only show three tickmarks on both axes + ax.yaxis.set_major_locator(ticker.MaxNLocator(4)) + if well not in plate.denatured_wells: # check if well is denatured (without determined Tm) + tm = well.tm # if not, grab its Tm + else: + tm = np.NaN # else set Tm to np.NaN + if tm: + ax.axvline(x=tm) # plot vertical line at the Tm + ax.axvspan(plate.t1, plate.tm_cutoff_low, facecolor='0.8', alpha=0.5) # shade lower cutoff area + ax.axvspan(plate.tm_cutoff_high, plate.t2, facecolor='0.8', alpha=0.5) # shade higher cutoff area + for label in ax.get_xticklabels() + ax.get_yticklabels(): # set fontsize for all tick labels to xx-small + label.set_fontsize('xx-small') + + cax = ax.plot(x, y) # plot data to the current subplot + canvas.draw() + + + def plot_raw(self, plate, widget): + """ + Plot raw data (Fig. 3) + """ + canvas = widget.canvas + canvas.clear() + fig3 = canvas.fig # new figure + fig3.suptitle('Raw Data (plate #{})'.format(str(plate.id))) # set title + + for plot_num in range(1, plate.wellnum + 1): # iterate over all wells + well = plate.wells[plot_num - 1] # get single well based on current plot number + ax = fig3.add_subplot(plate.rows, plate.cols, plot_num) # add new subplot + ax.autoscale(tight=True) # scale to data + ax.set_title(well.name, size='xx-small') # set title of current subplot to well identifier + + if well in plate.denatured_wells: + ax.patch.set_facecolor('#FFD6D6') + + if plot_num == plate.wellnum - plate.cols + 1: # add axis label to the subplot in the bottom left corner of the figure + ax.set_xlabel(u'T [°C]', size='xx-small') + ax.set_ylabel('I', size='xx-small') + + x = plate.temprange # set values for the x axis to the given temperature range + y = well.raw # grab y values from the raw data of the well + + ax.xaxis.set_major_locator(ticker.MaxNLocator(4)) # only show three tickmarks on both axes + ax.yaxis.set_major_locator(ticker.MaxNLocator(4)) + ax.axvspan(plate.t1, plate.tm_cutoff_low, facecolor='0.8', alpha=0.5) # shade lower cutoff area + ax.axvspan(plate.tm_cutoff_high, plate.t2, facecolor='0.8', alpha=0.5) # shade higher cutoff area + for label in ax.get_xticklabels() + ax.get_yticklabels(): # set fontsize for all tick labels to xx-small + label.set_fontsize('xx-small') + + cax = ax.plot(x, y) # plot data to the current subplot + canvas.draw() + + + # def _plot_wrapper(self, plot, plate): + # + # if plot == 'raw': + # fig, ax = self._plot_raw(plate) + # elif plot == 'derivative': + # fig, ax = self._plot_derivative(plate) + # elif plot == 'tm_heatmap': + # fig, ax = self._plot_tm_heatmap_single(plate) + # else: + # raise NotImplementedError + # fig = None + # ax = None + # return (fig, ax) + # + # def plot_all(self): + # + # figures = [] + # + # for plate in self.experiment.plates: + # + # figures.append(self._plot_wrapper('raw', plate)) + # figures.append(self._plot_wrapper('derivative', plate)) + # figures.append(self._plot_wrapper('tm_heatmap', plate)) + # + # if len(self.experiment.plates) > 1: + # figures.append(self._plot_wrapper('tm_heatmap', self.experiment.avg_plate)) + # + # return figures -#plate = Plate('/home/alex/Dokumente/Universitaet/Promotion/Denaturierung/26092012/26092012-MG.csv', type='analytikJena', cutoff_low=35.0, cutoff_high=60.0, signal_threshold=50000, color_range=(42, 50)) -#plot(plate) diff --git a/ui/Ui_mainwindow.py b/ui/Ui_mainwindow.py index 52a5903..e35db00 100644 --- a/ui/Ui_mainwindow.py +++ b/ui/Ui_mainwindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'mainwindow.ui' # -# Created: Fri Jan 30 14:07:06 2015 +# Created: Fri Jan 30 19:20:59 2015 # by: PyQt5 UI code generator 5.4 # # WARNING! All changes made in this file will be lost! @@ -12,7 +12,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(388, 642) + MainWindow.resize(808, 646) MainWindow.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)) self.centralWidget = QtWidgets.QWidget(MainWindow) self.centralWidget.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)) @@ -24,11 +24,11 @@ class Ui_MainWindow(object): self.groupBox_experiment.setFlat(False) self.groupBox_experiment.setCheckable(False) self.groupBox_experiment.setObjectName("groupBox_experiment") - self.gridLayout = QtWidgets.QGridLayout(self.groupBox_experiment) - self.gridLayout.setObjectName("gridLayout") + self.formLayout_3 = QtWidgets.QFormLayout(self.groupBox_experiment) + self.formLayout_3.setObjectName("formLayout_3") self.label_instrument = QtWidgets.QLabel(self.groupBox_experiment) self.label_instrument.setObjectName("label_instrument") - self.gridLayout.addWidget(self.label_instrument, 0, 0, 1, 1) + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_instrument) self.comboBox_instrument = QtWidgets.QComboBox(self.groupBox_experiment) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -37,7 +37,7 @@ class Ui_MainWindow(object): self.comboBox_instrument.setSizePolicy(sizePolicy) self.comboBox_instrument.setObjectName("comboBox_instrument") self.comboBox_instrument.addItem("") - self.gridLayout.addWidget(self.comboBox_instrument, 0, 1, 1, 1) + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_instrument) self.groupBox_data = QtWidgets.QGroupBox(self.groupBox_experiment) self.groupBox_data.setEnabled(True) self.groupBox_data.setObjectName("groupBox_data") @@ -72,7 +72,7 @@ class Ui_MainWindow(object): self.listWidget_data.setAlternatingRowColors(True) self.listWidget_data.setObjectName("listWidget_data") self.gridLayout_4.addWidget(self.listWidget_data, 0, 0, 2, 1) - self.gridLayout.addWidget(self.groupBox_data, 1, 0, 1, 2) + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.SpanningRole, self.groupBox_data) self.groupBox_temp = QtWidgets.QGroupBox(self.groupBox_experiment) self.groupBox_temp.setEnabled(True) self.groupBox_temp.setAutoFillBackground(False) @@ -108,7 +108,7 @@ class Ui_MainWindow(object): self.doubleSpinBox_dt.setProperty("value", 1.0) self.doubleSpinBox_dt.setObjectName("doubleSpinBox_dt") self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.doubleSpinBox_dt) - self.gridLayout.addWidget(self.groupBox_temp, 2, 0, 1, 1) + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.groupBox_temp) self.groupBox_cutoff = QtWidgets.QGroupBox(self.groupBox_experiment) self.groupBox_cutoff.setEnabled(True) self.groupBox_cutoff.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) @@ -135,7 +135,7 @@ class Ui_MainWindow(object): self.doubleSpinBox_lower.setDecimals(1) self.doubleSpinBox_lower.setObjectName("doubleSpinBox_lower") self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.doubleSpinBox_lower) - self.gridLayout.addWidget(self.groupBox_cutoff, 2, 1, 1, 1) + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.groupBox_cutoff) self.groupBox_signal_threshold = QtWidgets.QGroupBox(self.groupBox_experiment) self.groupBox_signal_threshold.setEnabled(True) self.groupBox_signal_threshold.setCheckable(True) @@ -147,7 +147,7 @@ class Ui_MainWindow(object): self.spinBox_signal_threshold.setMaximum(1000000) self.spinBox_signal_threshold.setObjectName("spinBox_signal_threshold") self.verticalLayout.addWidget(self.spinBox_signal_threshold) - self.gridLayout.addWidget(self.groupBox_signal_threshold, 3, 0, 1, 1) + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.groupBox_signal_threshold) self.groupBox_cbar = QtWidgets.QGroupBox(self.groupBox_experiment) self.groupBox_cbar.setEnabled(True) self.groupBox_cbar.setCheckable(True) @@ -170,15 +170,31 @@ class Ui_MainWindow(object): self.doubleSpinBox_cbar_end.setDecimals(1) self.doubleSpinBox_cbar_end.setObjectName("doubleSpinBox_cbar_end") self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.doubleSpinBox_cbar_end) - self.gridLayout.addWidget(self.groupBox_cbar, 3, 1, 1, 1) - self.gridLayout_2.addWidget(self.groupBox_experiment, 0, 0, 1, 1) - self.buttonBox_process = QtWidgets.QDialogButtonBox(self.centralWidget) + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.groupBox_cbar) + self.buttonBox_process = QtWidgets.QDialogButtonBox(self.groupBox_experiment) self.buttonBox_process.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) self.buttonBox_process.setObjectName("buttonBox_process") - self.gridLayout_2.addWidget(self.buttonBox_process, 1, 0, 1, 1) + self.formLayout_3.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.buttonBox_process) + self.gridLayout_2.addWidget(self.groupBox_experiment, 0, 0, 1, 1) + self.tabWidget = QtWidgets.QTabWidget(self.centralWidget) + self.tabWidget.setObjectName("tabWidget") + self.tab_raw = MplWidget() + self.tab_raw.setObjectName("tab_raw") + self.tabWidget.addTab(self.tab_raw, "") + self.tab_derivative = MplWidget() + self.tab_derivative.setObjectName("tab_derivative") + self.tabWidget.addTab(self.tab_derivative, "") + self.tab_heatmap = MplWidget() + self.tab_heatmap.setObjectName("tab_heatmap") + self.tabWidget.addTab(self.tab_heatmap, "") + self.tab_heatmap_avg = MplWidget() + self.tab_heatmap_avg.setEnabled(False) + self.tab_heatmap_avg.setObjectName("tab_heatmap_avg") + self.tabWidget.addTab(self.tab_heatmap_avg, "") + self.gridLayout_2.addWidget(self.tabWidget, 0, 1, 1, 1) MainWindow.setCentralWidget(self.centralWidget) self.menuBar = QtWidgets.QMenuBar(MainWindow) - self.menuBar.setGeometry(QtCore.QRect(0, 0, 388, 29)) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 808, 29)) self.menuBar.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)) self.menuBar.setObjectName("menuBar") self.menuFile = QtWidgets.QMenu(self.menuBar) @@ -215,6 +231,7 @@ class Ui_MainWindow(object): self.label_cbar_end.setBuddy(self.doubleSpinBox_cbar_end) self.retranslateUi(MainWindow) + self.tabWidget.setCurrentIndex(3) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): @@ -236,7 +253,7 @@ class Ui_MainWindow(object): self.label_dt.setText(_translate("MainWindow", "

ΔT

")) self.doubleSpinBox_dt.setSuffix(_translate("MainWindow", " °C")) self.groupBox_cutoff.setToolTip(_translate("MainWindow", "

Only Tm values within this limit are considered valid.

")) - self.groupBox_cutoff.setTitle(_translate("MainWindow", "Cutoff")) + self.groupBox_cutoff.setTitle(_translate("MainWindow", "&Cutoff")) self.label_cutoff_high.setText(_translate("MainWindow", "&Upper")) self.doubleSpinBox_upper.setSuffix(_translate("MainWindow", " °C")) self.label_cutoff_low.setText(_translate("MainWindow", "Lower")) @@ -249,8 +266,14 @@ class Ui_MainWindow(object): self.doubleSpinBox_cbar_start.setSuffix(_translate("MainWindow", " °C")) self.label_cbar_end.setText(_translate("MainWindow", "En&d")) self.doubleSpinBox_cbar_end.setSuffix(_translate("MainWindow", " °C")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_raw), _translate("MainWindow", "Raw Data")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_derivative), _translate("MainWindow", "&1st derivative")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_heatmap), _translate("MainWindow", "Heatmap")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_heatmap_avg), _translate("MainWindow", "Heatmap average")) self.menuFile.setTitle(_translate("MainWindow", "Fi&le")) - self.menuHelp.setTitle(_translate("MainWindow", "Help")) + self.menuHelp.setTitle(_translate("MainWindow", "Hel&p")) self.actionQuit.setText(_translate("MainWindow", "&Quit")) self.actionAbout.setText(_translate("MainWindow", "&About")) self.actionAbout_Qt.setText(_translate("MainWindow", "About &Qt")) + +from .mplwidget import MplWidget diff --git a/ui/icons.qrc b/ui/icons.qrc index f5af3f3..6bc09a8 100644 --- a/ui/icons.qrc +++ b/ui/icons.qrc @@ -1,5 +1,3 @@ - - qtlogo.svg - + diff --git a/ui/mainwindow.py b/ui/mainwindow.py index b41f2f6..adb4d0b 100644 --- a/ui/mainwindow.py +++ b/ui/mainwindow.py @@ -17,6 +17,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): """ Class documentation goes here. """ + def __init__(self, parent=None): """ Constructor @@ -33,6 +34,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.statusBar.showMessage("Welcome to PyDSF") + @pyqtSlot("QAbstractButton*") def on_buttonBox_open_reset_clicked(self, button): """ @@ -47,8 +49,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): elif button == self.buttonBox_open_reset.button(QDialogButtonBox.Reset): self.listWidget_data.clear() print("Data cleared") -# self.radioButton_rep_rows.setEnabled(False) -# self.radioButton_rep_columns.setEnabled(False) + # self.radioButton_rep_rows.setEnabled(False) + # self.radioButton_rep_columns.setEnabled(False) @pyqtSlot("QString") @@ -61,10 +63,10 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.groupBox_temp.setEnabled(True) else: self.groupBox_temp.setEnabled(False) -# self.groupBox_data.setEnabled(True) -# self.groupBox_cutoff.setEnabled(True) -# self.groupBox_cbar.setEnabled(True) -# self.groupBox_signal_threshold.setEnabled(True) + # self.groupBox_data.setEnabled(True) + # self.groupBox_cutoff.setEnabled(True) + # self.groupBox_cbar.setEnabled(True) + # self.groupBox_signal_threshold.setEnabled(True) @pyqtSlot() def on_buttonBox_process_accepted(self): @@ -76,7 +78,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): QMessageBox.critical(self, 'Error', "No data file loaded!", QMessageBox.Close, QMessageBox.Close) return if self.spinBox_signal_threshold.value() == 0 and self.groupBox_signal_threshold.isChecked(): - QMessageBox.warning(self, 'Warning', "Signal threshold is currently set to zero.", QMessageBox.Ok, QMessageBox.Ok) + QMessageBox.warning(self, 'Warning', "Signal threshold is currently set to zero.", QMessageBox.Ok, + QMessageBox.Ok) self.progressBar.setEnabled(True) self.statusBar.showMessage("Processing...") @@ -90,7 +93,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): c_lower = self.doubleSpinBox_lower.value() c_upper = self.doubleSpinBox_upper.value() if self.groupBox_cbar.isChecked(): - cbar_range = (self.doubleSpinBox_cbar_start, self.doubleSpinBox_cbar_end) + cbar_range = (self.doubleSpinBox_cbar_start, self.doubleSpinBox_cbar_end) if self.groupBox_signal_threshold.isChecked(): signal_threshold = self.spinBox_signal_threshold.value() @@ -99,29 +102,53 @@ class MainWindow(QMainWindow, Ui_MainWindow): files = [] for item in items: files.append(item.text()) - exp = Experiment(type=type, files=files, t1=self.doubleSpinBox_tmin.value(), t2=self.doubleSpinBox_tmax.value(), dt=self.doubleSpinBox_dt.value(), cols=12, rows=8, cutoff_low=c_lower, cutoff_high=c_upper, signal_threshold=signal_threshold, color_range=cbar_range) + exp = Experiment(type=type, files=files, t1=self.doubleSpinBox_tmin.value(), t2=self.doubleSpinBox_tmax.value(), + dt=self.doubleSpinBox_dt.value(), cols=12, rows=8, cutoff_low=c_lower, cutoff_high=c_upper, + signal_threshold=signal_threshold, color_range=cbar_range) exp.analyze() # plate = Plate(type=type, filename=self.lineEdit_data_file.text(), t1=self.doubleSpinBox_tmin.value(), t2=self.doubleSpinBox_tmax.value(), dt=self.doubleSpinBox_dt.value(), cols=12, rows=8, cutoff_low=c_lower, cutoff_high=c_upper, signal_threshold=signal_threshold, color_range=cbar_range) # self.statusBar.addWidget(self.pb, 100) - #plate.analyze(gui=self) + # plate.analyze(gui=self) save_data = QMessageBox.question(self, 'Save data', "Calculations are finished. Save results?", - QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes) + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if save_data == QMessageBox.Yes: dialog = QFileDialog() dialog.setFileMode(QFileDialog.Directory) folder = dialog.getExistingDirectory(self, 'Choose path for results') for plate in exp.plates: - plate.write_tm_table('{}/plate_{}_04_tm.csv'.format(folder, str(plate.id))) - plate.write_derivative_table('{}/plate_{}_03_dI_dT.csv'.format(folder, str(plate.id))) - plate.write_filtered_table('{}/plate_{}_02_filtered_data.csv'.format(folder, str(plate.id))) - plate.write_raw_table('{}/plate_{}_01_raw_data.csv'.format(folder, str(plate.id))) + plate.write_tm_table('{}/plate_{}_04_tm.csv'.format(folder, str(plate.id))) + plate.write_derivative_table('{}/plate_{}_03_dI_dT.csv'.format(folder, str(plate.id))) + plate.write_filtered_table('{}/plate_{}_02_filtered_data.csv'.format(folder, str(plate.id))) + plate.write_raw_table('{}/plate_{}_01_raw_data.csv'.format(folder, str(plate.id))) if exp.avg_plate: - exp.avg_plate.write_avg_tm_table('{}/plate_{}_05_tm_avg.csv'.format(folder, str(exp.avg_plate.id))) - #plot(plate, self) + exp.avg_plate.write_avg_tm_table('{}/plate_{}_05_tm_avg.csv'.format(folder, str(exp.avg_plate.id))) + #plot(plate, self) - plot(exp) + plotter = PlotResults(exp) + for i in range(self.tabWidget.count()): + for plate in exp.plates: + if i == 0: + plotter.plot_raw(plate, self.tabWidget.widget(i)) + elif i == 1: + plotter.plot_derivative(plate, self.tabWidget.widget(i)) + elif i == 2: + plotter.plot_tm_heatmap_single(plate, self.tabWidget.widget(i)) + elif exp.avg_plate and i == 3: + plotter.plot_tm_heatmap_single(exp.avg_plate, self.tabWidget.widget(i)) + self.tabWidget.setTabEnabled(i, True) + else: + self.tabWidget.setTabEnabled(i, False) + + #for i in range(self.tabWidget.count()): + # self.tabWidget.widget(i).canvas.clear() + + #fig, ax = figures[0] + #self.tabWidget.widget(0).canvas.fig = fig + #self.tabWidget.widget(0).canvas.ax = ax + + #self.tabWidget.widget(0).canvas.draw() self.progressBar.setEnabled(False) self.statusBar.showMessage("Finished!") @@ -135,6 +162,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): QApplication.quit() pyqtSlot() + def on_actionQuit_triggered(self): """ Slot documentation goes here. diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 0b0a627..44dc5a5 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 388 - 642 + 808 + 646 @@ -35,7 +35,7 @@ false - + @@ -247,7 +247,7 @@ <html><head/><body><p>Only T<span style=" vertical-align:sub;">m</span> values within this limit are considered valid.</p></body></html> - Cutoff + &Cutoff Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -406,14 +406,44 @@ + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + 3 + + + Raw Data + + + + + &1st derivative + + + + + Heatmap + + + + + false + + + Heatmap average + + @@ -423,7 +453,7 @@ 0 0 - 388 + 808 29 @@ -444,7 +474,7 @@ - Help + Hel&p @@ -465,7 +495,7 @@ - + :/qtlogo.svg:/qtlogo.svg @@ -473,6 +503,14 @@ + + + MplWidget + QWidget +
mplwidget
+ 1 +
+
diff --git a/ui/mplwidget.py b/ui/mplwidget.py new file mode 100644 index 0000000..58f446b --- /dev/null +++ b/ui/mplwidget.py @@ -0,0 +1,25 @@ +from PyQt5 import QtWidgets +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt5agg import NavigationToolbar2QTAgg as NavigationToolbar +from matplotlib.figure import Figure + +class MplCanvas(FigureCanvas): + def __init__(self): + self.fig = Figure() + self.ax = self.fig.add_subplot(111) + FigureCanvas.__init__(self, self.fig) + FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + FigureCanvas.updateGeometry(self) + + def clear(self): + self.ax.clear() + self.fig.clear() + +class MplWidget(QtWidgets.QWidget): + def __init__(self, parent = None): + QtWidgets.QWidget.__init__(self, parent) + + self.canvas = MplCanvas() + self.vbl = QtWidgets.QVBoxLayout() + self.vbl.addWidget(self.canvas) + self.setLayout(self.vbl) \ No newline at end of file