From 6a6bef6c287e2b1cf768ef82251bfd27e625c739 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Thu, 29 Aug 2024 11:18:59 +0100 Subject: [PATCH 01/39] Add docstrings --- src/idvc/ui/widgets.py | 21 +++++++++++++++++++++ src/idvc/ui/windows.py | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 8cf37f41..97b3354b 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -21,6 +21,12 @@ class SingleRunResultsWidget(QtWidgets.QWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' def __init__(self, parent, plot_data, displ_wrt_point0 = False): + ''' + Parameters + ---------- + plot_data: RunResults + displ_wrt_point0: bool + ''' super().__init__() self.parent = parent @@ -37,9 +43,23 @@ def __init__(self, parent, plot_data, displ_wrt_point0 = False): self.CreateHistogram(plot_data, displ_wrt_point0) def CreateHistogram(self, result, displ_wrt_point0): + ''' + Gets the filepath of the disp file via `result.disp_file`. Extracts the displacements by using the converter. + This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. + 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. + Hist makes an instogram with bins 20. + + Parameters + ---------- + result: RunResults + displ_wrt_point0: bool + ''' + + print("result.disp_file is ",result.disp_file) displ = np.asarray( PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] ) + print("displ is",displ) if displ_wrt_point0: point0_disp = [displ[0][6],displ[0][7], displ[0][8]] for count in range(len(displ)): @@ -47,6 +67,7 @@ def CreateHistogram(self, result, displ_wrt_point0): displ[count][i+6] = displ[count][i+6] - point0_disp[i] plot_data = [displ[:,i] for i in range(5, displ.shape[1])] + print("plot_data is",plot_data) numGraphs = len(plot_data) if numGraphs <= 3: diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index d36da894..69de1abf 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -233,6 +233,8 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): file_path = os.path.join(folder, os.path.basename(folder)) result = RunResults(file_path) result_list.append(result) + print("result is",result) + print("dispwrt is",displ_wrt_point0) GraphWidget = SingleRunResultsWidget(self, result, displ_wrt_point0) dock1 = QDockWidget(result.title,self) From 89442d6f0de832bcf910a11307a5ad9429ea8cc7 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 11 Sep 2024 17:28:29 +0100 Subject: [PATCH 02/39] Edit plots and methods --- src/idvc/dvc_interface.py | 3 +- src/idvc/ui/widgets.py | 71 +++++++++-------------- src/idvc/ui/windows.py | 4 +- src/idvc/utilities.py | 16 ++--- src/idvc/utils/manipulate_result_files.py | 23 ++++++++ 5 files changed, 64 insertions(+), 53 deletions(-) create mode 100644 src/idvc/utils/manipulate_result_files.py diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index d89b825f..e979d366 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -4959,8 +4959,7 @@ def show_run_pcs(self): points_list = [] subvol_list = [] for folder in glob.glob(os.path.join(directory, "dvc_result_*")): - file_path = os.path.join(folder, os.path.basename(folder)) - result = RunResults(file_path) + result = RunResults(folder) self.result_list.append(result) el = str(result.subvol_points) if el not in points_list: diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 97b3354b..f3a1dae6 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -8,6 +8,7 @@ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from idvc.pointcloud_conversion import PointCloudConverter +from idvc.utils.manipulate_result_files import extractDataFromDispResultFile from functools import partial import shutil import os @@ -20,17 +21,18 @@ class SingleRunResultsWidget(QtWidgets.QWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent, plot_data, displ_wrt_point0 = False): + def __init__(self, parent, result, displ_wrt_point0 = False): ''' Parameters ---------- - plot_data: RunResults + results: RunResults displ_wrt_point0: bool ''' super().__init__() self.parent = parent self.figure = plt.figure() + plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) @@ -40,13 +42,14 @@ def __init__(self, parent, plot_data, displ_wrt_point0 = False): self.layout.addWidget(self.canvas) self.setLayout(self.layout) - self.CreateHistogram(plot_data, displ_wrt_point0) + self.CreateHistogram(result, displ_wrt_point0) + + def CreateHistogram(self, result, displ_wrt_point0): ''' - Gets the filepath of the disp file via `result.disp_file`. Extracts the displacements by using the converter. - This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. - 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. + Extracts the data from the disp file. + Determines the number of graphs, rows and column to group them in. Hist makes an instogram with bins 20. Parameters @@ -54,22 +57,8 @@ def CreateHistogram(self, result, displ_wrt_point0): result: RunResults displ_wrt_point0: bool ''' - - print("result.disp_file is ",result.disp_file) - displ = np.asarray( - PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] - ) - print("displ is",displ) - if displ_wrt_point0: - point0_disp = [displ[0][6],displ[0][7], displ[0][8]] - for count in range(len(displ)): - for i in range(3): - displ[count][i+6] = displ[count][i+6] - point0_disp[i] - - plot_data = [displ[:,i] for i in range(5, displ.shape[1])] - print("plot_data is",plot_data) - - numGraphs = len(plot_data) + data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) + numGraphs = len(result_arrays) if numGraphs <= 3: numRows = 1 else: @@ -77,13 +66,20 @@ def CreateHistogram(self, result, displ_wrt_point0): numColumns = np.ceil(numGraphs/numRows) plotNum = 0 - for array in plot_data: + print("no_points",no_points) + relative_frequency_data = result_arrays + for array in relative_frequency_data: plotNum = plotNum + 1 + xlabel = result.data_label[plotNum-1] ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) - ax.set_ylabel("") - #ax.set_xlabel(plot_titles[plotNum-1]) - ax.set_title(result.plot_titles[plotNum-1]) - ax.hist(array,20) + counts, bins, patches = ax.hist(array, bins=20) + relative_counts = counts*100/ no_points + print(relative_counts) + ax.clear() + bin_widths = np.diff(bins) + ax.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') + ax.set_ylabel("Relative frequency (% points in run)") + ax.set_xlabel(xlabel) plt.tight_layout() # Provides proper spacing between figures @@ -121,20 +117,20 @@ def __init__(self, parent, result_list, displ_wrt_point0 = False): self.label = QLabel(self) - self.label.setText("Select which variable would like to compare: ") + self.label.setText("Select data: ") self.layout.addWidget(self.label,widgetno,1) self.combo = QComboBox(self) - self.combo.addItems(result.plot_titles) + self.combo.addItems(result.data_label) self.layout.addWidget(self.combo,widgetno,2) widgetno+=1 self.label1 = QLabel(self) - self.label1.setText("Select which parameter you would like to compare: ") + self.label1.setText("Select parameter: ") self.layout.addWidget(self.label1,widgetno,1) self.combo1 = QComboBox(self) - self.param_list = ["All","Sampling Points in Subvolume", "Subvolume Size"] + self.param_list = ["All","Sampling points in subvolume", "Subvolume size"] self.combo1.addItems(self.param_list) self.layout.addWidget(self.combo1,widgetno,2) widgetno+=1 @@ -215,16 +211,7 @@ def CreateHistogram(self, result_list, displ_wrt_point0): displacements = [] for result in result_list: - displ = np.asarray( - PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] - ) - if displ_wrt_point0: - point0_disp = [displ[0][6],displ[0][7], displ[0][8]] - for count in range(len(displ)): - for i in range(3): - displ[count][i+6] = displ[count][i+6] - point0_disp[i] - - no_points = np.shape(displ[0]) + data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) if no_points not in points_list: points_list.append(no_points) @@ -238,7 +225,7 @@ def CreateHistogram(self, result_list, displ_wrt_point0): pass resultsToPlot.append(result) - displacements.append(displ) + displacements.append(result_arrays) points_list.sort() diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 69de1abf..3b45dddc 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -215,7 +215,7 @@ def __init__(self, parent=None): def SetResultsFolder(self, folder): self.results_folder = folder - self.setWindowTitle("Digital Volume Correlation Results - {foldername}".format(foldername=os.path.basename(self.results_folder))) + self.setWindowTitle("Run {foldername}".format(foldername=os.path.basename(self.results_folder))) def ReloadGraphs(self): self.DeleteAllWidgets() @@ -231,7 +231,7 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): #print(results_folder[0]) for folder in glob.glob(os.path.join(self.results_folder, "dvc_result_*")): file_path = os.path.join(folder, os.path.basename(folder)) - result = RunResults(file_path) + result = RunResults(folder) result_list.append(result) print("result is",result) print("dispwrt is",displ_wrt_point0) diff --git a/src/idvc/utilities.py b/src/idvc/utilities.py index 3449c7b1..b51c565c 100644 --- a/src/idvc/utilities.py +++ b/src/idvc/utilities.py @@ -4,16 +4,17 @@ from PySide2.QtCore import * from PySide2.QtGui import * import numpy as np +import os class RunResults(object): - def __init__(self, file_name): - + def __init__(self, folder): + file_name = os.path.join(folder, os.path.basename(folder)) self.points = None - disp_file_name = file_name + ".disp" stat_file_name = file_name + ".stat" + print("disp file name",disp_file_name) with open(stat_file_name,"r") as stat_file: @@ -46,19 +47,20 @@ def __init__(self, file_name): # self.subvol_aspect = [int(line.split('\t')[1]),int(line.split('\t')[2]), int(line.split('\t')[3])] count+=1 - plot_titles_dict = { + data_label_dict = { 'objmin': "Objective Minimum", 'u': "Displacement in x", 'v':"Displacement in y", 'w':"Displacement in z", 'phi':"Change in phi",'the':"Change in theta", 'psi':"Change in psi"} with open(disp_file_name) as f: # first 4 columns are: n, x, y, z, status - we don't want these - self.plot_titles = f.readline().split()[5:] - self.plot_titles = [plot_titles_dict.get(text, text) for text in self.plot_titles] + self.data_label = f.readline().split()[5:] + self.data_label = [data_label_dict.get(text, text) for text in self.data_label] self.disp_file = disp_file_name + self.run_name = os.path.basename(os.path.dirname(folder)) - self.title = str(self.subvol_points) + " Points in Subvolume," + " Subvolume Size: " + str(self.subvol_size) + self.title = str(self.subvol_points) + "," + str(self.subvol_size) def __str__(self): diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py new file mode 100644 index 00000000..0d4d02c4 --- /dev/null +++ b/src/idvc/utils/manipulate_result_files.py @@ -0,0 +1,23 @@ +import numpy as np +from idvc.pointcloud_conversion import PointCloudConverter + +def extractDataFromDispResultFile(result, displ_wrt_point0): + """ + Gets the filepath of the disp file via `result.disp_file`. + Extracts the data in the numpy format by using the converter, does not load the label row. + This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. + 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. + """ + print("result.disp_file is ", result.disp_file) + data = np.asarray( + PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] + ) + data_shape = data.shape + index_objmin = 5 + index_disp = [6,9] + no_points = data_shape[0] + if displ_wrt_point0: + point0_disp_array = data[0,index_disp[0]:index_disp[1]] + data[:,index_disp[0]:index_disp[1]] = data[:,index_disp[0]:index_disp[1]] - point0_disp_array + result_arrays = np.transpose(data[:,index_objmin:data_shape[1]]) + return data, no_points, result_arrays \ No newline at end of file From c69a147bfcbf49af20b54fda05bb61998024cc7f Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Thu, 12 Sep 2024 15:09:05 +0100 Subject: [PATCH 03/39] Add statistics --- src/idvc/ui/widgets.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index f3a1dae6..6c596a8d 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -14,6 +14,7 @@ import os import tempfile from eqt.threading import Worker +from scipy.stats import norm @@ -67,8 +68,7 @@ def CreateHistogram(self, result, displ_wrt_point0): plotNum = 0 print("no_points",no_points) - relative_frequency_data = result_arrays - for array in relative_frequency_data: + for array in result_arrays: plotNum = plotNum + 1 xlabel = result.data_label[plotNum-1] ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) @@ -80,6 +80,20 @@ def CreateHistogram(self, result, displ_wrt_point0): ax.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') ax.set_ylabel("Relative frequency (% points in run)") ax.set_xlabel(xlabel) + mean = array.mean() + var = array.var() + std = array.std() + #print("variance is",array.var()) + ax.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') + ax.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') + ax.axvline(mean+std, color='g', linestyle='--') + + # Plot the Gaussian curve + x = np.linspace(min(array), max(array), 1000) + gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 + ax.plot(x, gaussian, 'b--', label='gaussian fit') + + ax.legend(loc='upper right') plt.tight_layout() # Provides proper spacing between figures From 948acacaac15d941c658c9e0e5ea764bca9d9141 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Thu, 12 Sep 2024 18:18:10 +0100 Subject: [PATCH 04/39] Add superclass and xlabels --- src/idvc/ui/widgets.py | 94 +++++++++++++++++++++++------------------- src/idvc/utilities.py | 3 +- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 6c596a8d..1e140dc8 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -16,13 +16,10 @@ from eqt.threading import Worker from scipy.stats import norm - - - -class SingleRunResultsWidget(QtWidgets.QWidget): - '''creates a dockable widget which will display results from a single run of the DVC code +class BaseResultsWidget(QtWidgets.QWidget): + '''creates a dockable widget which will display graph results from runs of the DVC code ''' - def __init__(self, parent, result, displ_wrt_point0 = False): + def __init__(self, parent): ''' Parameters ---------- @@ -31,23 +28,60 @@ def __init__(self, parent, result, displ_wrt_point0 = False): ''' super().__init__() self.parent = parent + self.plt = plt + self.figure = self.plt.figure() - self.figure = plt.figure() - plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) + def addSubplot(self, figure, numRows, numColumns, plotNum, result, array): + '''plot the Gaussian curve, legend''' + xlabel = result.data_label[plotNum-1] + ax = figure.add_subplot(numRows, numColumns, int(plotNum)) + counts, bins, patches = ax.hist(array, bins=20) + relative_counts = counts*100/ len(array) + + ax.clear() + bin_widths = np.diff(bins) + ax.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') + ax.set_ylabel("Relative frequency (% points in run)") + ax.set_xlabel(xlabel) + + mean = array.mean() + var = array.var() + std = array.std() + ax.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') + ax.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') + ax.axvline(mean+std, color='g', linestyle='--') + + x = np.linspace(min(array), max(array), 1000) + gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 + ax.plot(x, gaussian, 'b--', label='gaussian fit') + + ax.legend(loc='upper right') + +class SingleRunResultsWidget(BaseResultsWidget): + '''creates a dockable widget which will display results from a single run of the DVC code + ''' + def __init__(self, parent, result, displ_wrt_point0 = False): + ''' + Parameters + ---------- + results: RunResults + displ_wrt_point0: bool + ''' + super().__init__(parent) + self.plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") + #Layout self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas) self.setLayout(self.layout) - self.CreateHistogram(result, displ_wrt_point0) - + self.addHistogramsToLayout(result, displ_wrt_point0) - - def CreateHistogram(self, result, displ_wrt_point0): + def addHistogramsToLayout(self, result, displ_wrt_point0): ''' Extracts the data from the disp file. Determines the number of graphs, rows and column to group them in. @@ -67,43 +101,19 @@ def CreateHistogram(self, result, displ_wrt_point0): numColumns = np.ceil(numGraphs/numRows) plotNum = 0 - print("no_points",no_points) for array in result_arrays: plotNum = plotNum + 1 - xlabel = result.data_label[plotNum-1] - ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) - counts, bins, patches = ax.hist(array, bins=20) - relative_counts = counts*100/ no_points - print(relative_counts) - ax.clear() - bin_widths = np.diff(bins) - ax.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') - ax.set_ylabel("Relative frequency (% points in run)") - ax.set_xlabel(xlabel) - mean = array.mean() - var = array.var() - std = array.std() - #print("variance is",array.var()) - ax.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') - ax.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') - ax.axvline(mean+std, color='g', linestyle='--') - - # Plot the Gaussian curve - x = np.linspace(min(array), max(array), 1000) - gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 - ax.plot(x, gaussian, 'b--', label='gaussian fit') - - ax.legend(loc='upper right') + self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, array) plt.tight_layout() # Provides proper spacing between figures self.canvas.draw() -class SummaryGraphsWidget(QtWidgets.QWidget): +class SummaryGraphsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' def __init__(self, parent, result_list, displ_wrt_point0 = False): - super().__init__() + super().__init__(parent) self.parent = parent #Layout @@ -260,6 +270,7 @@ def CreateHistogram(self, result_list, displ_wrt_point0): row = self.subvol_points.index(result.subvol_points) + 1 column= self.subvol_sizes.index(result.subvol_size) + 1 plotNum = (row-1)*numColumns + column + ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) if row ==1: @@ -280,9 +291,8 @@ def CreateHistogram(self, result_list, displ_wrt_point0): plot_data = [displacements[i][:,k] for k in range(5, displacements[i].shape[1])] - #get variable to display graphs for: - ax.hist(plot_data[self.combo.currentIndex()], 20) - + self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, plot_data[self.combo.currentIndex()]) + self.figure.suptitle(self.combo.currentText(),size ="large") plt.tight_layout() # Provides proper spacing between figures diff --git a/src/idvc/utilities.py b/src/idvc/utilities.py index b51c565c..f2d23217 100644 --- a/src/idvc/utilities.py +++ b/src/idvc/utilities.py @@ -48,7 +48,7 @@ def __init__(self, folder): count+=1 data_label_dict = { - 'objmin': "Objective Minimum", 'u': "Displacement in x", 'v':"Displacement in y", 'w':"Displacement in z", + 'objmin': "Objective minimum", 'u': "Displacement x component (pixels)", 'v':"Displacement y component (pixels)", 'w':"Displacement z component (pixels)", 'phi':"Change in phi",'the':"Change in theta", 'psi':"Change in psi"} with open(disp_file_name) as f: @@ -56,7 +56,6 @@ def __init__(self, folder): self.data_label = f.readline().split()[5:] self.data_label = [data_label_dict.get(text, text) for text in self.data_label] - self.disp_file = disp_file_name self.run_name = os.path.basename(os.path.dirname(folder)) From a26adfd21ef182c27d5156fe8bd4b2f6ee6881c4 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Fri, 13 Sep 2024 13:55:48 +0100 Subject: [PATCH 05/39] Restructure code --- src/idvc/dvc_interface.py | 6 +- src/idvc/ui/widgets.py | 162 ++++++++++++++++++++------------------ src/idvc/ui/windows.py | 19 ++--- 3 files changed, 97 insertions(+), 90 deletions(-) diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index e979d366..65686499 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -89,9 +89,9 @@ from idvc import version as gui_version -from idvc.ui.widgets import * +from idvc.ui.graphs_widgets import * from idvc.ui.dialogs import * -from idvc.ui.windows import * +from idvc.ui.VisualisationWidget import * from idvc.utilities import * __version__ = gui_version.version @@ -487,7 +487,7 @@ def CreateHelpPanel(self): "4. Limit the range of the vectors viewed by changing the 'Vector Range Min' and Vector Range Max'. Then, click 'View Pointcloud/Vectors' to apply the changes.\n" "5. On the 2D viewer, the vectors are shown as 2D arrows, showing the displacements in the current plane. If the 'x', 'y' or 'z' keys are pressed click 'View Pointcloud/Vectors' to apply the changes.\n\n" "Display Graphs:\n" - "Graphs are displayed in a new window (once you are done looking at the graphs you can either close or minimize this window). A tab is created for each run, showing a summary of the parameters.\n" + "Graphs are displayed in a new window (once you are done looking at the graphs you can either close or minimize this window). A tab is created for each run and an additional tab for the bulk run.\n" "1. Select an option from the list for the variable to compare.\n" "2. Select the parameters to compare from the list.\n" "3. Click on 'Plot Histograms'.\n" diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 1e140dc8..282a9e3e 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -1,4 +1,3 @@ -import PySide2 from PySide2 import QtWidgets, QtCore from PySide2.QtWidgets import * from PySide2.QtCore import * @@ -7,7 +6,6 @@ import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar -from idvc.pointcloud_conversion import PointCloudConverter from idvc.utils.manipulate_result_files import extractDataFromDispResultFile from functools import partial import shutil @@ -15,6 +13,9 @@ import tempfile from eqt.threading import Worker from scipy.stats import norm +import glob +from idvc.utilities import RunResults + class BaseResultsWidget(QtWidgets.QWidget): '''creates a dockable widget which will display graph results from runs of the DVC code @@ -34,6 +35,8 @@ def __init__(self, parent): self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) + + def addSubplot(self, figure, numRows, numColumns, plotNum, result, array): '''plot the Gaussian curve, legend''' xlabel = result.data_label[plotNum-1] @@ -63,7 +66,7 @@ def addSubplot(self, figure, numRows, numColumns, plotNum, result, array): class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent, result, displ_wrt_point0 = False): + def __init__(self, parent): ''' Parameters ---------- @@ -71,18 +74,17 @@ def __init__(self, parent, result, displ_wrt_point0 = False): displ_wrt_point0: bool ''' super().__init__(parent) - self.plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") - + + #Layout self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas) self.setLayout(self.layout) - self.addHistogramsToLayout(result, displ_wrt_point0) - - def addHistogramsToLayout(self, result, displ_wrt_point0): + def addHistogramsToLayout(self, result, displ_wrt_point0 = False): ''' + Extracts the data from the disp file. Determines the number of graphs, rows and column to group them in. Hist makes an instogram with bins 20. @@ -92,6 +94,7 @@ def addHistogramsToLayout(self, result, displ_wrt_point0): result: RunResults displ_wrt_point0: bool ''' + self.plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) numGraphs = len(result_arrays) if numGraphs <= 3: @@ -109,21 +112,42 @@ def addHistogramsToLayout(self, result, displ_wrt_point0): self.canvas.draw() -class SummaryGraphsWidget(BaseResultsWidget): +class BulkRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' - def __init__(self, parent, result_list, displ_wrt_point0 = False): + def __init__(self, parent, folder, displ_wrt_point0 = False): super().__init__(parent) self.parent = parent - #Layout self.layout = QtWidgets.QGridLayout() #self.layout.setSpacing(1) self.layout.setAlignment(Qt.AlignTop) + + + + + self.figure = plt.figure() + result_list = self.importResultList(folder) + self.addWidgetstoLayout(result_list) + self.button.clicked.connect(partial(self.addHistogramsToLayout,result_list, displ_wrt_point0)) + + + self.setLayout(self.layout) + self.addHistogramsToLayout(result_list, displ_wrt_point0) + + def importResultList(self, results_folder): + result_list=[] + for folder in glob.glob(os.path.join(results_folder, "dvc_result_*")): + result = RunResults(folder) + result_list.append(result) + return result_list + + def addWidgetstoLayout(self, result_list): widgetno=0 if len(result_list) >=1: + print(result_list) result = result_list[0] #These options were the same for all runs: self.results_details_label = QLabel(self) @@ -144,32 +168,21 @@ def __init__(self, parent, result_list, displ_wrt_point0 = False): self.label.setText("Select data: ") self.layout.addWidget(self.label,widgetno,1) - self.combo = QComboBox(self) - self.combo.addItems(result.data_label) - self.layout.addWidget(self.combo,widgetno,2) + self.data_label_widget = QComboBox(self) + self.data_label_widget.addItems(result.data_label) + self.layout.addWidget(self.data_label_widget,widgetno,2) widgetno+=1 self.label1 = QLabel(self) self.label1.setText("Select parameter: ") self.layout.addWidget(self.label1,widgetno,1) - self.combo1 = QComboBox(self) + self.param_list_widget = QComboBox(self) self.param_list = ["All","Sampling points in subvolume", "Subvolume size"] - self.combo1.addItems(self.param_list) - self.layout.addWidget(self.combo1,widgetno,2) + self.param_list_widget.addItems(self.param_list) + self.layout.addWidget(self.param_list_widget,widgetno,2) widgetno+=1 - self.subvol_points=[] - self.subvol_sizes=[] - - for result in result_list: - if result.subvol_points not in self.subvol_points: - self.subvol_points.append(result.subvol_points) - if result.subvol_size not in self.subvol_sizes: - self.subvol_sizes.append(result.subvol_size) - self.subvol_points.sort() - self.subvol_sizes.sort() - self.secondParamLabel = QLabel(self) self.secondParamLabel.setText("Subvolume size:") self.layout.addWidget(self.secondParamLabel,widgetno,1) @@ -180,17 +193,15 @@ def __init__(self, parent, result_list, displ_wrt_point0 = False): self.layout.addWidget(self.secondParamCombo,widgetno,2) widgetno+=1 - self.combo1.currentIndexChanged.connect(self.showSecondParam) + self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) self.secondParamLabel.hide() self.secondParamCombo.hide() self.button = QtWidgets.QPushButton("Plot Histograms") - self.button.clicked.connect(partial(self.CreateHistogram,result_list, displ_wrt_point0)) + self.layout.addWidget(self.button,widgetno,2) widgetno+=1 - self.figure = plt.figure() - self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.layout.addWidget(self.toolbar,widgetno,0,1,3) @@ -198,10 +209,8 @@ def __init__(self, parent, result_list, displ_wrt_point0 = False): self.layout.addWidget(self.canvas,widgetno,0,3,3) widgetno+=1 - self.setLayout(self.layout) - def showSecondParam(self): - index = self.combo1.currentIndex() + index = self.param_list_widget.currentIndex() if index ==0: self.secondParamLabel.hide() self.secondParamCombo.hide() @@ -221,57 +230,59 @@ def showSecondParam(self): newList = [] self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) - - def CreateHistogram(self, result_list, displ_wrt_point0): + def addHistogramsToLayout(self, result_list, displ_wrt_point0): - self.figure.clear() + self.subvol_points=[] + self.subvol_sizes=[] + + for result in result_list: + if result.subvol_points not in self.subvol_points: + self.subvol_points.append(result.subvol_points) + if result.subvol_size not in self.subvol_sizes: + self.subvol_sizes.append(result.subvol_size) + self.subvol_points.sort() + self.subvol_sizes.sort() - index = self.combo1.currentIndex() - - points_list = [] - resultsToPlot= [] + self.figure.clear() - displacements = [] + param_index = self.param_list_widget.currentIndex() for result in result_list: - data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) - if no_points not in points_list: - points_list.append(no_points) - if index == 1: # Points in subvolume is compared + + + if param_index == 1: # Points in subvolume is compared if result.subvol_size != float(self.secondParamCombo.currentText()): pass - elif index ==2: + elif param_index ==2: if result.subvol_points != float(self.secondParamCombo.currentText()): pass - - resultsToPlot.append(result) - displacements.append(result_arrays) - points_list.sort() + no_points_list.sort() - if index ==0: - numRows = len(self.subvol_points) - numColumns = len(self.subvol_sizes) + if param_index == 1 or param_index ==2: + numRows = 1 + numColumns = len(result_list) - else: - if len(resultsToPlot) <= 3: - numRows = 1 - else: - numRows = np.round(np.sqrt(len(resultsToPlot))) - numColumns = np.ceil(len(resultsToPlot)/numRows) + no_points_list = [] + for result in result_list: + if no_points not in no_points_list: + no_points_list.append(no_points) - plotNum = 0 - for i, result in enumerate(resultsToPlot): - if index ==0: - row = self.subvol_points.index(result.subvol_points) + 1 - column= self.subvol_sizes.index(result.subvol_size) + 1 - plotNum = (row-1)*numColumns + column - - ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) + if param_index ==0: + plotNum = 0 + + for result in result_list: + plotNum = plotNum + 1 + data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) + subvol_size = result.subvol_size + subvol_points = result.subvol_points + + self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, result_arrays[self.data_label_widget.currentIndex()]) + if row ==1: ax.set_title("Subvolume Size:" + str(result.subvol_size) ) @@ -283,23 +294,24 @@ def CreateHistogram(self, result_list, displ_wrt_point0): plotNum = plotNum + 1 ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) - if index ==1: + if param_index ==1: text = str(result.subvol_points) - if index ==2: + if param_index ==2: text = str(result.subvol_size) - ax.set_ylabel(text + " " + self.combo1.currentText()) + ax.set_ylabel(text + " " + self.param_list_widget.currentText()) - plot_data = [displacements[i][:,k] for k in range(5, displacements[i].shape[1])] + plot_data = [result_arrays_list[i][:,k] for k in range(5, result_arrays_list[i].shape[1])] - self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, plot_data[self.combo.currentIndex()]) + self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, plot_data[self.data_label_widget.currentIndex()]) - self.figure.suptitle(self.combo.currentText(),size ="large") + self.figure.suptitle(self.data_label_widget.currentText(),size ="large") plt.tight_layout() # Provides proper spacing between figures plt.subplots_adjust(top=0.88) # Means heading doesn't overlap with subplot titles self.canvas.draw() + class SaveObjectWindow(QtWidgets.QWidget): '''a window which will appear when saving a mask or pointcloud ''' diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 3b45dddc..893e8a46 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -227,16 +227,10 @@ def DeleteAllWidgets(self): del current_dock def CreateDockWidgets(self, displ_wrt_point0 = False): - result_list=[] - #print(results_folder[0]) for folder in glob.glob(os.path.join(self.results_folder, "dvc_result_*")): - file_path = os.path.join(folder, os.path.basename(folder)) + GraphWidget = SingleRunResultsWidget(self) result = RunResults(folder) - result_list.append(result) - print("result is",result) - print("dispwrt is",displ_wrt_point0) - - GraphWidget = SingleRunResultsWidget(self, result, displ_wrt_point0) + GraphWidget.addHistogramsToLayout(result, displ_wrt_point0) dock1 = QDockWidget(result.title,self) dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) @@ -253,17 +247,18 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): self.tabifyDockWidget(prev,current_dock) prev= current_dock - SummaryTab = SummaryGraphsWidget(self, result_list) - dock = QDockWidget("Summary",self) + #BulkTab = BulkRunResultsWidget(self, self.results_folder, displ_wrt_point0) + dock = QDockWidget("Bulk",self) dock.setFeatures(QDockWidget.NoDockWidgetFeatures) dock.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - dock.setWidget(SummaryTab) + #dock.setWidget(BulkTab) self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock) self.tabifyDockWidget(prev,dock) - dock.raise_() # makes summary panel the one that is open by default. + dock.raise_() # makes bulk panel the one that is open by default. # Stop the widgets in the tab to be moved around for wdg in self.findChildren(QTabBar): wdg.setMovable(False) + From e4fae29b158ecacee248773b542765d4df94c0a6 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Fri, 13 Sep 2024 16:23:34 +0100 Subject: [PATCH 06/39] Add pandas and dataframe --- recipe/dev_environment.yml | 1 + recipe/meta.yaml | 1 + src/idvc/dvc_interface.py | 4 +- src/idvc/ui/widgets.py | 75 ++++++++++------------- src/idvc/ui/windows.py | 31 ++++++++-- src/idvc/utilities.py | 1 - src/idvc/utils/manipulate_result_files.py | 1 - 7 files changed, 62 insertions(+), 52 deletions(-) diff --git a/recipe/dev_environment.yml b/recipe/dev_environment.yml index 48fc875f..0bbdca56 100644 --- a/recipe/dev_environment.yml +++ b/recipe/dev_environment.yml @@ -6,6 +6,7 @@ dependencies: - openpyxl - python - numpy + - pandas - scipy - ccpi::ccpi-viewer >=24.0.1 - ccpi::ccpi-dvc >=22.0.0 diff --git a/recipe/meta.yaml b/recipe/meta.yaml index be7d96b0..8a4a6782 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -37,6 +37,7 @@ requirements: - openpyxl - python - numpy + - pandas - scipy - ccpi-viewer >=24.0.1 - ccpi-dvc >=22.0.0 diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index 65686499..98b3b078 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -89,9 +89,9 @@ from idvc import version as gui_version -from idvc.ui.graphs_widgets import * +from idvc.ui.widgets import * from idvc.ui.dialogs import * -from idvc.ui.VisualisationWidget import * +from idvc.ui.windows import * from idvc.utilities import * __version__ = gui_version.version diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 282a9e3e..c7a60429 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -62,6 +62,7 @@ def addSubplot(self, figure, numRows, numColumns, plotNum, result, array): ax.plot(x, gaussian, 'b--', label='gaussian fit') ax.legend(loc='upper right') + return ax class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code @@ -75,7 +76,6 @@ def __init__(self, parent): ''' super().__init__(parent) - #Layout self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.toolbar) @@ -106,16 +106,17 @@ def addHistogramsToLayout(self, result, displ_wrt_point0 = False): plotNum = 0 for array in result_arrays: plotNum = plotNum + 1 - self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, array) + subplot = self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, array) plt.tight_layout() # Provides proper spacing between figures self.canvas.draw() + return subplot class BulkRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' - def __init__(self, parent, folder, displ_wrt_point0 = False): + def __init__(self, parent, result_data_frame, displ_wrt_point0 = False): super().__init__(parent) self.parent = parent @@ -128,40 +129,32 @@ def __init__(self, parent, folder, displ_wrt_point0 = False): self.figure = plt.figure() - result_list = self.importResultList(folder) - self.addWidgetstoLayout(result_list) - self.button.clicked.connect(partial(self.addHistogramsToLayout,result_list, displ_wrt_point0)) + single_result = result_data_frame.iloc[0]['result'] + self.subvol_sizes = result_data_frame['subvol_size'].unique() + print(single_result) + print(result_data_frame['subvol_size']) + self.addWidgetstoLayout(single_result) + #self.button.clicked.connect(partial(self.addHistogramsToLayout, result_data_frame, displ_wrt_point0)) self.setLayout(self.layout) - self.addHistogramsToLayout(result_list, displ_wrt_point0) - - def importResultList(self, results_folder): - result_list=[] - for folder in glob.glob(os.path.join(results_folder, "dvc_result_*")): - result = RunResults(folder) - result_list.append(result) - return result_list + #self.addHistogramsToLayout(result_data_frame, displ_wrt_point0) - def addWidgetstoLayout(self, result_list): + def addWidgetstoLayout(self, result): widgetno=0 - if len(result_list) >=1: - print(result_list) - result = result_list[0] #These options were the same for all runs: - - self.results_details_label = QLabel(self) - self.results_details_label.setText("Subvolume Geometry: {subvol_geom}\n\ + self.results_details_label = QLabel(self) + self.results_details_label.setText("Subvolume Geometry: {subvol_geom}\n\ Maximum Displacement: {disp_max}\n\ Degrees of Freedom: {num_srch_dof}\n\ Objective Function: {obj_function}\n\ Interpolation Type: {interp_type}\n\ Rigid Body Offset: {rigid_trans}".format(subvol_geom=result.subvol_geom, \ - disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ - interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) - self.layout.addWidget(self.results_details_label,widgetno,0,5,1) - self.results_details_label.setAlignment(Qt.AlignTop) - widgetno+=1 + disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ + interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) + self.layout.addWidget(self.results_details_label,widgetno,0,5,1) + self.results_details_label.setAlignment(Qt.AlignTop) + widgetno+=1 self.label = QLabel(self) @@ -187,15 +180,14 @@ def addWidgetstoLayout(self, result_list): self.secondParamLabel.setText("Subvolume size:") self.layout.addWidget(self.secondParamLabel,widgetno,1) - self.secondParamCombo = QComboBox(self) - self.secondParamList = [str(i) for i in self.subvol_sizes] - self.secondParamCombo.addItems(self.secondParamList) - self.layout.addWidget(self.secondParamCombo,widgetno,2) + self.subvolSizesCombo = QComboBox(self) + self.subvolSizesCombo.addItems(self.subvol_sizes) + self.layout.addWidget(self.subvolSizesCombo,widgetno,2) widgetno+=1 self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) self.secondParamLabel.hide() - self.secondParamCombo.hide() + self.subvolSizesCombo.hide() self.button = QtWidgets.QPushButton("Plot Histograms") @@ -203,7 +195,6 @@ def addWidgetstoLayout(self, result_list): widgetno+=1 self.canvas = FigureCanvas(self.figure) - self.toolbar = NavigationToolbar(self.canvas, self) self.layout.addWidget(self.toolbar,widgetno,0,1,3) widgetno+=1 self.layout.addWidget(self.canvas,widgetno,0,3,3) @@ -213,24 +204,24 @@ def showSecondParam(self): index = self.param_list_widget.currentIndex() if index ==0: self.secondParamLabel.hide() - self.secondParamCombo.hide() + self.subvolSizesCombo.hide() elif index == 1: self.secondParamLabel.show() - self.secondParamCombo.show() + self.subvolSizesCombo.show() self.secondParamLabel.setText("Subvolume Size:") - self.secondParamCombo.clear() - self.secondParamCombo.addItems([str(i) for i in self.subvol_sizes]) + self.subvolSizesCombo.clear() + self.subvolSizesCombo.addItems([str(i) for i in self.subvol_sizes]) elif index == 2: self.secondParamLabel.show() - self.secondParamCombo.show() + self.subvolSizesCombo.show() self.secondParamLabel.setText("Points in Subvolume:") - self.secondParamCombo.clear() + self.subvolSizesCombo.clear() newList = [] - self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) + self.subvolSizesCombo.addItems([str(i) for i in self.subvol_points]) - def addHistogramsToLayout(self, result_list, displ_wrt_point0): + def addHistogramsToLayout(self, result_data_frame, displ_wrt_point0): self.subvol_points=[] self.subvol_sizes=[] @@ -254,11 +245,11 @@ def addHistogramsToLayout(self, result_list, displ_wrt_point0): if param_index == 1: # Points in subvolume is compared - if result.subvol_size != float(self.secondParamCombo.currentText()): + if result.subvol_size != float(self.subvolSizesCombo.currentText()): pass elif param_index ==2: - if result.subvol_points != float(self.secondParamCombo.currentText()): + if result.subvol_points != float(self.subvolSizesCombo.currentText()): pass no_points_list.sort() diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 893e8a46..e4c09e47 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -20,6 +20,8 @@ from idvc.utilities import RunResults import logging +import json +import pandas as pd class VisualisationWindow(QtWidgets.QMainWindow): @@ -226,16 +228,33 @@ def DeleteAllWidgets(self): current_dock.close() del current_dock - def CreateDockWidgets(self, displ_wrt_point0 = False): + def CreateDockWidgets(self, displ_wrt_point0 = False): + subvol_size_list = [] + subvol_points_list = [] + result_list = [] + plot_list = [] for folder in glob.glob(os.path.join(self.results_folder, "dvc_result_*")): - GraphWidget = SingleRunResultsWidget(self) + single_run_results_widget = SingleRunResultsWidget(self) result = RunResults(folder) - GraphWidget.addHistogramsToLayout(result, displ_wrt_point0) + + plot = single_run_results_widget.addHistogramsToLayout(result, displ_wrt_point0) dock1 = QDockWidget(result.title,self) dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - dock1.setWidget(GraphWidget) + dock1.setWidget(single_run_results_widget) self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock1) + + subvol_size_list.append(str(result.subvol_size)) + subvol_points_list.append(str(result.subvol_points)) + result_list.append(result) + plot_list.append(plot) + + result_data_frame = pd.DataFrame({ + 'subvol_size': subvol_size_list, + 'subvol_points': subvol_points_list, + 'result': result_list, + 'plot': plot_list}) + print(result_data_frame) prev = None @@ -247,11 +266,11 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): self.tabifyDockWidget(prev,current_dock) prev= current_dock - #BulkTab = BulkRunResultsWidget(self, self.results_folder, displ_wrt_point0) + bulk_run_results_widget = BulkRunResultsWidget(self, result_data_frame, displ_wrt_point0) dock = QDockWidget("Bulk",self) dock.setFeatures(QDockWidget.NoDockWidgetFeatures) dock.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - #dock.setWidget(BulkTab) + dock.setWidget(bulk_run_results_widget) self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock) self.tabifyDockWidget(prev,dock) diff --git a/src/idvc/utilities.py b/src/idvc/utilities.py index f2d23217..bd94bb9b 100644 --- a/src/idvc/utilities.py +++ b/src/idvc/utilities.py @@ -14,7 +14,6 @@ def __init__(self, folder): self.points = None disp_file_name = file_name + ".disp" stat_file_name = file_name + ".stat" - print("disp file name",disp_file_name) with open(stat_file_name,"r") as stat_file: diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py index 0d4d02c4..ba87c6b8 100644 --- a/src/idvc/utils/manipulate_result_files.py +++ b/src/idvc/utils/manipulate_result_files.py @@ -8,7 +8,6 @@ def extractDataFromDispResultFile(result, displ_wrt_point0): This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. """ - print("result.disp_file is ", result.disp_file) data = np.asarray( PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] ) From bedaf07db1a37b515710fea0b8403a36a9c46231 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 16 Sep 2024 14:34:11 +0100 Subject: [PATCH 07/39] Many edits, bulk graphs working well --- src/idvc/ui/widgets.py | 185 +++++++++------------- src/idvc/ui/windows.py | 45 ++---- src/idvc/utils/manipulate_result_files.py | 27 +++- 3 files changed, 113 insertions(+), 144 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index c7a60429..96e61652 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -4,6 +4,7 @@ from PySide2.QtGui import * import numpy as np import matplotlib.pyplot as plt +from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from idvc.utils.manipulate_result_files import extractDataFromDispResultFile @@ -13,8 +14,6 @@ import tempfile from eqt.threading import Worker from scipy.stats import norm -import glob -from idvc.utilities import RunResults class BaseResultsWidget(QtWidgets.QWidget): @@ -29,45 +28,49 @@ def __init__(self, parent): ''' super().__init__() self.parent = parent - self.plt = plt - self.figure = self.plt.figure() - - self.canvas = FigureCanvas(self.figure) + self.fig = Figure() + self.fig, hist_plot = plt.subplots() + self.canvas = FigureCanvas(self.fig) + self.fig.clf() self.toolbar = NavigationToolbar(self.canvas, self) - def addSubplot(self, figure, numRows, numColumns, plotNum, result, array): - '''plot the Gaussian curve, legend''' - xlabel = result.data_label[plotNum-1] - ax = figure.add_subplot(numRows, numColumns, int(plotNum)) - counts, bins, patches = ax.hist(array, bins=20) - relative_counts = counts*100/ len(array) - ax.clear() + def addSubplot(self, plotNum, result, array, data_label): + '''plot the Gaussian curve, legend + + Returns + ------- + matplotlib.pyplot + A plot of the histogram + """''' + xlabel = data_label + counts, bins, patches = plt.hist(array, bins=20) + relative_counts = counts*100/ len(array) + plt.cla() bin_widths = np.diff(bins) - ax.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') - ax.set_ylabel("Relative frequency (% points in run)") - ax.set_xlabel(xlabel) + plt.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') + plt.ylabel("Relative frequency (% points in run)") + plt.xlabel(xlabel) mean = array.mean() - var = array.var() std = array.std() - ax.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') - ax.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') - ax.axvline(mean+std, color='g', linestyle='--') + plt.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') + plt.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') + plt.axvline(mean+std, color='g', linestyle='--') x = np.linspace(min(array), max(array), 1000) gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 - ax.plot(x, gaussian, 'b--', label='gaussian fit') + plt.plot(x, gaussian, 'b--', label='gaussian fit') - ax.legend(loc='upper right') - return ax + plt.legend(loc='upper right') + return plt class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent): + def __init__(self, parent, result, displ_wrt_point0): ''' Parameters ---------- @@ -81,8 +84,10 @@ def __init__(self, parent): self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas) self.setLayout(self.layout) + self.addHistogramsToLayout(result, displ_wrt_point0) + - def addHistogramsToLayout(self, result, displ_wrt_point0 = False): + def addHistogramsToLayout(self, result, displ_wrt_point0): ''' Extracts the data from the disp file. @@ -94,21 +99,26 @@ def addHistogramsToLayout(self, result, displ_wrt_point0 = False): result: RunResults displ_wrt_point0: bool ''' - self.plt.suptitle(f"Run {result.run_name}: points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") - data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) + self.fig.suptitle(f"Run '{result.run_name}': points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") + result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) numGraphs = len(result_arrays) if numGraphs <= 3: numRows = 1 else: - numRows = np.round(np.sqrt(numGraphs)) - numColumns = np.ceil(numGraphs/numRows) + numRows = int(np.round(np.sqrt(numGraphs))) + print(numRows) + numColumns = int(np.ceil(numGraphs/numRows)) + print(numColumns) plotNum = 0 for array in result_arrays: + data_label = result.data_label[plotNum] plotNum = plotNum + 1 - subplot = self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, array) - - plt.tight_layout() # Provides proper spacing between figures + self.fig.add_subplot(numRows, numColumns, plotNum) + subplot = self.addSubplot(plotNum, result, array, data_label) + + + self.fig.tight_layout() # Provides proper spacing between figures self.canvas.draw() return subplot @@ -116,7 +126,7 @@ def addHistogramsToLayout(self, result, displ_wrt_point0 = False): class BulkRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' - def __init__(self, parent, result_data_frame, displ_wrt_point0 = False): + def __init__(self, parent, result_data_frame): super().__init__(parent) self.parent = parent @@ -124,21 +134,18 @@ def __init__(self, parent, result_data_frame, displ_wrt_point0 = False): #self.layout.setSpacing(1) self.layout.setAlignment(Qt.AlignTop) - - - - - self.figure = plt.figure() single_result = result_data_frame.iloc[0]['result'] + self.run_name = single_result.run_name self.subvol_sizes = result_data_frame['subvol_size'].unique() + self.subvol_points = result_data_frame['subvol_points'].unique() print(single_result) print(result_data_frame['subvol_size']) self.addWidgetstoLayout(single_result) - #self.button.clicked.connect(partial(self.addHistogramsToLayout, result_data_frame, displ_wrt_point0)) + self.button.clicked.connect(partial(self.addHistogramsToLayout, result_data_frame)) self.setLayout(self.layout) - #self.addHistogramsToLayout(result_data_frame, displ_wrt_point0) + self.addHistogramsToLayout(result_data_frame) def addWidgetstoLayout(self, result): widgetno=0 @@ -194,7 +201,6 @@ def addWidgetstoLayout(self, result): self.layout.addWidget(self.button,widgetno,2) widgetno+=1 - self.canvas = FigureCanvas(self.figure) self.layout.addWidget(self.toolbar,widgetno,0,1,3) widgetno+=1 self.layout.addWidget(self.canvas,widgetno,0,3,3) @@ -221,84 +227,39 @@ def showSecondParam(self): newList = [] self.subvolSizesCombo.addItems([str(i) for i in self.subvol_points]) - def addHistogramsToLayout(self, result_data_frame, displ_wrt_point0): - - self.subvol_points=[] - self.subvol_sizes=[] - - for result in result_list: - if result.subvol_points not in self.subvol_points: - self.subvol_points.append(result.subvol_points) - if result.subvol_size not in self.subvol_sizes: - self.subvol_sizes.append(result.subvol_size) - self.subvol_points.sort() - self.subvol_sizes.sort() - - - self.figure.clear() - + def addHistogramsToLayout(self, result_data_frame): + self.fig.clf() param_index = self.param_list_widget.currentIndex() - - for result in result_list: - - - - - if param_index == 1: # Points in subvolume is compared - if result.subvol_size != float(self.subvolSizesCombo.currentText()): - pass - - elif param_index ==2: - if result.subvol_points != float(self.subvolSizesCombo.currentText()): - pass - - no_points_list.sort() - - if param_index == 1 or param_index ==2: + + self.fig.suptitle(f"Bulk Run '{self.run_name}': {self.data_label_widget.currentText()}") + + if param_index == 1: numRows = 1 - numColumns = len(result_list) - - no_points_list = [] - for result in result_list: - if no_points not in no_points_list: - no_points_list.append(no_points) + numColumns = len(result_data_frame) + + elif param_index ==2: + numRows = 1 + numColumns = len(self.subvol_points) - if param_index ==0: - plotNum = 0 + elif param_index ==0: - for result in result_list: - plotNum = plotNum + 1 - data, no_points, result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) - subvol_size = result.subvol_size - subvol_points = result.subvol_points - - self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, result_arrays[self.data_label_widget.currentIndex()]) - - if row ==1: - ax.set_title("Subvolume Size:" + str(result.subvol_size) ) - if column == 1: - text = str(result.subvol_points) - ax.set_ylabel(text + " " + "Points in subvol") - - else: + numRows = len(self.subvol_points) + numColumns = len(self.subvol_sizes) + + plotNum = 0 + + for row in result_data_frame.itertuples(): + print(f"Index: {row.Index}") + print(f"Row data: {row.result}") + data_label = f"{self.data_label_widget.currentText()}" + data_index = self.data_label_widget.currentIndex() plotNum = plotNum + 1 - ax = self.figure.add_subplot(int(numRows), int(numColumns), int(plotNum)) - - if param_index ==1: - text = str(result.subvol_points) - if param_index ==2: - text = str(result.subvol_size) - ax.set_ylabel(text + " " + self.param_list_widget.currentText()) - - plot_data = [result_arrays_list[i][:,k] for k in range(5, result_arrays_list[i].shape[1])] - - self.addSubplot(self.figure, int(numRows), int(numColumns), plotNum, result, plot_data[self.data_label_widget.currentIndex()]) + self.fig.add_subplot(numRows, numColumns, plotNum) + self.addSubplot(plotNum, row.result, row.result_arrays[data_index], data_label) - self.figure.suptitle(self.data_label_widget.currentText(),size ="large") - - plt.tight_layout() # Provides proper spacing between figures - plt.subplots_adjust(top=0.88) # Means heading doesn't overlap with subplot titles + self.fig.tight_layout() # Provides proper spacing between figures + self.fig.subplots_adjust(top=0.88) # Means heading doesn't overlap with subplot titles self.canvas.draw() diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index e4c09e47..3bfd7cd6 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -22,6 +22,7 @@ import logging import json import pandas as pd +from idvc.utils.manipulate_result_files import createResultsDataFrame class VisualisationWindow(QtWidgets.QMainWindow): @@ -229,33 +230,16 @@ def DeleteAllWidgets(self): del current_dock def CreateDockWidgets(self, displ_wrt_point0 = False): - subvol_size_list = [] - subvol_points_list = [] - result_list = [] - plot_list = [] - for folder in glob.glob(os.path.join(self.results_folder, "dvc_result_*")): - single_run_results_widget = SingleRunResultsWidget(self) - result = RunResults(folder) - - plot = single_run_results_widget.addHistogramsToLayout(result, displ_wrt_point0) + result_data_frame = createResultsDataFrame(self.results_folder, displ_wrt_point0) + print(result_data_frame) + for result in result_data_frame['result']: + single_run_results_widget = SingleRunResultsWidget(self, result, displ_wrt_point0) dock1 = QDockWidget(result.title,self) dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) dock1.setWidget(single_run_results_widget) self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock1) - subvol_size_list.append(str(result.subvol_size)) - subvol_points_list.append(str(result.subvol_points)) - result_list.append(result) - plot_list.append(plot) - - result_data_frame = pd.DataFrame({ - 'subvol_size': subvol_size_list, - 'subvol_points': subvol_points_list, - 'result': result_list, - 'plot': plot_list}) - print(result_data_frame) - prev = None for current_dock in self.findChildren(QDockWidget): @@ -266,15 +250,16 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): self.tabifyDockWidget(prev,current_dock) prev= current_dock - bulk_run_results_widget = BulkRunResultsWidget(self, result_data_frame, displ_wrt_point0) - dock = QDockWidget("Bulk",self) - dock.setFeatures(QDockWidget.NoDockWidgetFeatures) - dock.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - dock.setWidget(bulk_run_results_widget) - self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock) - self.tabifyDockWidget(prev,dock) - - dock.raise_() # makes bulk panel the one that is open by default. + if len(result_data_frame) > 1: + bulk_run_results_widget = BulkRunResultsWidget(self, result_data_frame) + dock = QDockWidget("Bulk",self) + dock.setFeatures(QDockWidget.NoDockWidgetFeatures) + dock.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) + dock.setWidget(bulk_run_results_widget) + self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock) + self.tabifyDockWidget(prev,dock) + + dock.raise_() # makes bulk panel the one that is open by default. # Stop the widgets in the tab to be moved around for wdg in self.findChildren(QTabBar): diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py index ba87c6b8..aade3c2b 100644 --- a/src/idvc/utils/manipulate_result_files.py +++ b/src/idvc/utils/manipulate_result_files.py @@ -1,5 +1,8 @@ import numpy as np +import pandas as pd from idvc.pointcloud_conversion import PointCloudConverter +from idvc.utilities import RunResults +import glob, os def extractDataFromDispResultFile(result, displ_wrt_point0): """ @@ -14,9 +17,29 @@ def extractDataFromDispResultFile(result, displ_wrt_point0): data_shape = data.shape index_objmin = 5 index_disp = [6,9] - no_points = data_shape[0] if displ_wrt_point0: point0_disp_array = data[0,index_disp[0]:index_disp[1]] data[:,index_disp[0]:index_disp[1]] = data[:,index_disp[0]:index_disp[1]] - point0_disp_array result_arrays = np.transpose(data[:,index_objmin:data_shape[1]]) - return data, no_points, result_arrays \ No newline at end of file + return result_arrays + +def createResultsDataFrame(results_folder, displ_wrt_point0): + subvol_size_list = [] + subvol_points_list = [] + result_list = [] + result_arrays_list = [] + + for folder in glob.glob(os.path.join(results_folder, "dvc_result_*")): + result = RunResults(folder) + result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) + subvol_size_list.append(str(result.subvol_size)) + subvol_points_list.append(str(result.subvol_points)) + result_list.append(result) + + result_arrays_list.append(result_arrays) + result_data_frame = pd.DataFrame({ +'subvol_size': subvol_size_list, +'subvol_points': subvol_points_list, +'result': result_list, +'result_arrays': result_arrays_list}) + return result_data_frame \ No newline at end of file From 6c812fcd54bd44ca4b12e600a7895ed4934a62ba Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 16 Sep 2024 18:07:30 +0100 Subject: [PATCH 08/39] Improve layout --- src/idvc/ui/widgets.py | 82 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 96e61652..85e15bad 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -14,6 +14,7 @@ import tempfile from eqt.threading import Worker from scipy.stats import norm +from eqt.ui.NoBorderScrollArea import NoBorderScrollArea class BaseResultsWidget(QtWidgets.QWidget): @@ -21,6 +22,10 @@ class BaseResultsWidget(QtWidgets.QWidget): ''' def __init__(self, parent): ''' + Adds vertical layout. + figure. + canvas. + sets minimum size so scrollbar works. Parameters ---------- results: RunResults @@ -29,10 +34,14 @@ def __init__(self, parent): super().__init__() self.parent = parent self.fig = Figure() - self.fig, hist_plot = plt.subplots() + self.fig, hist_plot = plt.subplots(figsize=(10, 8), layout="constrained") self.canvas = FigureCanvas(self.fig) self.fig.clf() + self.canvas.setMinimumSize(400, 400) #needed for scrollbar self.toolbar = NavigationToolbar(self.canvas, self) + self.layout = QtWidgets.QVBoxLayout() + self.layout.addWidget(self.canvas) + self.setLayout(self.layout) @@ -78,11 +87,8 @@ def __init__(self, parent, result, displ_wrt_point0): displ_wrt_point0: bool ''' super().__init__(parent) - - #Layout - self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.toolbar) - self.layout.addWidget(self.canvas) + self.layout.addWidget(self.canvas, stretch=1) self.setLayout(self.layout) self.addHistogramsToLayout(result, displ_wrt_point0) @@ -99,16 +105,15 @@ def addHistogramsToLayout(self, result, displ_wrt_point0): result: RunResults displ_wrt_point0: bool ''' - self.fig.suptitle(f"Run '{result.run_name}': points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}") + self.fig.suptitle(f"Run '{result.run_name}': points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}",fontsize='xx-large') result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) numGraphs = len(result_arrays) if numGraphs <= 3: numRows = 1 else: numRows = int(np.round(np.sqrt(numGraphs))) - print(numRows) numColumns = int(np.ceil(numGraphs/numRows)) - print(numColumns) + plotNum = 0 for array in result_arrays: @@ -117,8 +122,7 @@ def addHistogramsToLayout(self, result, displ_wrt_point0): self.fig.add_subplot(numRows, numColumns, plotNum) subplot = self.addSubplot(plotNum, result, array, data_label) - - self.fig.tight_layout() # Provides proper spacing between figures + self.fig.subplots_adjust(hspace=0.5,wspace=0.5) self.canvas.draw() return subplot @@ -128,26 +132,25 @@ class BulkRunResultsWidget(BaseResultsWidget): ''' def __init__(self, parent, result_data_frame): super().__init__(parent) - self.parent = parent - self.layout = QtWidgets.QGridLayout() - #self.layout.setSpacing(1) - self.layout.setAlignment(Qt.AlignTop) + + self.grid_layout = QtWidgets.QGridLayout() + self.grid_layout.setAlignment(Qt.AlignTop) + self.layout.addLayout(self.grid_layout,0) single_result = result_data_frame.iloc[0]['result'] self.run_name = single_result.run_name self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() - print(single_result) - print(result_data_frame['subvol_size']) - self.addWidgetstoLayout(single_result) + self.addWidgetstoGridLayout(single_result) self.button.clicked.connect(partial(self.addHistogramsToLayout, result_data_frame)) - - self.setLayout(self.layout) + scroll_area_widget = NoBorderScrollArea(self.canvas) + self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.layout.addWidget(scroll_area_widget,1) self.addHistogramsToLayout(result_data_frame) - def addWidgetstoLayout(self, result): + def addWidgetstoGridLayout(self, result): widgetno=0 self.results_details_label = QLabel(self) @@ -159,37 +162,37 @@ def addWidgetstoLayout(self, result): Rigid Body Offset: {rigid_trans}".format(subvol_geom=result.subvol_geom, \ disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) - self.layout.addWidget(self.results_details_label,widgetno,0,5,1) + self.grid_layout.addWidget(self.results_details_label,widgetno,0,5,1) self.results_details_label.setAlignment(Qt.AlignTop) widgetno+=1 self.label = QLabel(self) self.label.setText("Select data: ") - self.layout.addWidget(self.label,widgetno,1) + self.grid_layout.addWidget(self.label,widgetno,1) self.data_label_widget = QComboBox(self) self.data_label_widget.addItems(result.data_label) - self.layout.addWidget(self.data_label_widget,widgetno,2) + self.grid_layout.addWidget(self.data_label_widget,widgetno,2) widgetno+=1 self.label1 = QLabel(self) self.label1.setText("Select parameter: ") - self.layout.addWidget(self.label1,widgetno,1) + self.grid_layout.addWidget(self.label1,widgetno,1) self.param_list_widget = QComboBox(self) self.param_list = ["All","Sampling points in subvolume", "Subvolume size"] self.param_list_widget.addItems(self.param_list) - self.layout.addWidget(self.param_list_widget,widgetno,2) + self.grid_layout.addWidget(self.param_list_widget,widgetno,2) widgetno+=1 self.secondParamLabel = QLabel(self) self.secondParamLabel.setText("Subvolume size:") - self.layout.addWidget(self.secondParamLabel,widgetno,1) + self.grid_layout.addWidget(self.secondParamLabel,widgetno,1) self.subvolSizesCombo = QComboBox(self) self.subvolSizesCombo.addItems(self.subvol_sizes) - self.layout.addWidget(self.subvolSizesCombo,widgetno,2) + self.grid_layout.addWidget(self.subvolSizesCombo,widgetno,2) widgetno+=1 self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) @@ -198,12 +201,10 @@ def addWidgetstoLayout(self, result): self.button = QtWidgets.QPushButton("Plot Histograms") - self.layout.addWidget(self.button,widgetno,2) + self.grid_layout.addWidget(self.button,widgetno,2) widgetno+=1 - self.layout.addWidget(self.toolbar,widgetno,0,1,3) - widgetno+=1 - self.layout.addWidget(self.canvas,widgetno,0,3,3) + self.grid_layout.addWidget(self.toolbar,widgetno,0,1,3) widgetno+=1 def showSecondParam(self): @@ -231,7 +232,7 @@ def addHistogramsToLayout(self, result_data_frame): self.fig.clf() param_index = self.param_list_widget.currentIndex() - self.fig.suptitle(f"Bulk Run '{self.run_name}': {self.data_label_widget.currentText()}") + self.fig.suptitle(f"Bulk Run '{self.run_name}': {self.data_label_widget.currentText()}",fontsize='xx-large') if param_index == 1: numRows = 1 @@ -244,22 +245,21 @@ def addHistogramsToLayout(self, result_data_frame): elif param_index ==0: - numRows = len(self.subvol_points) - numColumns = len(self.subvol_sizes) + numRows = len(self.subvol_sizes) + numColumns = len(self.subvol_points) plotNum = 0 for row in result_data_frame.itertuples(): - print(f"Index: {row.Index}") - print(f"Row data: {row.result}") + result = row.result data_label = f"{self.data_label_widget.currentText()}" data_index = self.data_label_widget.currentIndex() plotNum = plotNum + 1 - self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(plotNum, row.result, row.result_arrays[data_index], data_label) - - self.fig.tight_layout() # Provides proper spacing between figures - self.fig.subplots_adjust(top=0.88) # Means heading doesn't overlap with subplot titles + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label) + subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) + + self.fig.subplots_adjust(hspace=2,wspace=0.5) self.canvas.draw() From 2f70f1076be257652b21335be8c82fd63e2b0171 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 17 Sep 2024 16:08:01 +0100 Subject: [PATCH 09/39] Add selection of parameters --- src/idvc/ui/widgets.py | 65 ++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 85e15bad..fd31dfa4 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -190,14 +190,14 @@ def addWidgetstoGridLayout(self, result): self.secondParamLabel.setText("Subvolume size:") self.grid_layout.addWidget(self.secondParamLabel,widgetno,1) - self.subvolSizesCombo = QComboBox(self) - self.subvolSizesCombo.addItems(self.subvol_sizes) - self.grid_layout.addWidget(self.subvolSizesCombo,widgetno,2) + self.secondParamCombo = QComboBox(self) + self.secondParamCombo.addItems(self.subvol_sizes) + self.grid_layout.addWidget(self.secondParamCombo,widgetno,2) widgetno+=1 self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) self.secondParamLabel.hide() - self.subvolSizesCombo.hide() + self.secondParamCombo.hide() self.button = QtWidgets.QPushButton("Plot Histograms") @@ -211,22 +211,22 @@ def showSecondParam(self): index = self.param_list_widget.currentIndex() if index ==0: self.secondParamLabel.hide() - self.subvolSizesCombo.hide() + self.secondParamCombo.hide() elif index == 1: self.secondParamLabel.show() - self.subvolSizesCombo.show() + self.secondParamCombo.show() self.secondParamLabel.setText("Subvolume Size:") - self.subvolSizesCombo.clear() - self.subvolSizesCombo.addItems([str(i) for i in self.subvol_sizes]) + self.secondParamCombo.clear() + self.secondParamCombo.addItems([str(i) for i in self.subvol_sizes]) elif index == 2: self.secondParamLabel.show() - self.subvolSizesCombo.show() + self.secondParamCombo.show() self.secondParamLabel.setText("Points in Subvolume:") - self.subvolSizesCombo.clear() + self.secondParamCombo.clear() newList = [] - self.subvolSizesCombo.addItems([str(i) for i in self.subvol_points]) + self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) def addHistogramsToLayout(self, result_data_frame): self.fig.clf() @@ -234,31 +234,28 @@ def addHistogramsToLayout(self, result_data_frame): self.fig.suptitle(f"Bulk Run '{self.run_name}': {self.data_label_widget.currentText()}",fontsize='xx-large') - if param_index == 1: - numRows = 1 - numColumns = len(result_data_frame) + numRows = len(self.subvol_sizes) + numColumns = len(self.subvol_points) + plotNum = 0 - elif param_index ==2: - numRows = 1 - numColumns = len(self.subvol_points) + for row in result_data_frame.itertuples(): + result = row.result + + if param_index == 1: + numRows = 1 + if result.subvol_size != float(self.secondParamCombo.currentText()): + continue + elif param_index ==2: + numColumns = 1 + if result.subvol_points != float(self.secondParamCombo.currentText()): + continue + data_label = f"{self.data_label_widget.currentText()}" + data_index = self.data_label_widget.currentIndex() + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label) + subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) - elif param_index ==0: - - - numRows = len(self.subvol_sizes) - numColumns = len(self.subvol_points) - - plotNum = 0 - - for row in result_data_frame.itertuples(): - result = row.result - data_label = f"{self.data_label_widget.currentText()}" - data_index = self.data_label_widget.currentIndex() - plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label) - subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) - self.fig.subplots_adjust(hspace=2,wspace=0.5) self.canvas.draw() From de0abaca57469c00c835ae48f5372896391c0285 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 17 Sep 2024 18:13:36 +0100 Subject: [PATCH 10/39] Add mean and std in data frame and create statistical analysis tab --- src/idvc/ui/widgets.py | 45 +++++++++++++---------- src/idvc/ui/windows.py | 36 ++++++++++++------ src/idvc/utils/manipulate_result_files.py | 15 ++++++++ 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index fd31dfa4..7e191c3d 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -46,7 +46,7 @@ def __init__(self, parent): - def addSubplot(self, plotNum, result, array, data_label): + def addSubplot(self, plotNum, result, array, data_label, mean, std): '''plot the Gaussian curve, legend Returns @@ -62,11 +62,8 @@ def addSubplot(self, plotNum, result, array, data_label): plt.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') plt.ylabel("Relative frequency (% points in run)") plt.xlabel(xlabel) - - mean = array.mean() - std = array.std() - plt.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.2f}') - plt.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.2f}') + plt.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.3f}') + plt.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.3f}') plt.axvline(mean+std, color='g', linestyle='--') x = np.linspace(min(array), max(array), 1000) @@ -74,12 +71,11 @@ def addSubplot(self, plotNum, result, array, data_label): plt.plot(x, gaussian, 'b--', label='gaussian fit') plt.legend(loc='upper right') - return plt class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent, result, displ_wrt_point0): + def __init__(self, parent, result, displ_wrt_point0, mean_array, std_array): ''' Parameters ---------- @@ -90,10 +86,10 @@ def __init__(self, parent, result, displ_wrt_point0): self.layout.addWidget(self.toolbar) self.layout.addWidget(self.canvas, stretch=1) self.setLayout(self.layout) - self.addHistogramsToLayout(result, displ_wrt_point0) + self.addHistogramsToLayout(result, displ_wrt_point0, mean_array, std_array) - def addHistogramsToLayout(self, result, displ_wrt_point0): + def addHistogramsToLayout(self, result, displ_wrt_point0, mean_array, std_array): ''' Extracts the data from the disp file. @@ -115,17 +111,24 @@ def addHistogramsToLayout(self, result, displ_wrt_point0): numColumns = int(np.ceil(numGraphs/numRows)) + plotNum = 0 for array in result_arrays: data_label = result.data_label[plotNum] + mean = mean_array[plotNum] + std = std_array[plotNum] plotNum = plotNum + 1 self.fig.add_subplot(numRows, numColumns, plotNum) - subplot = self.addSubplot(plotNum, result, array, data_label) + self.addSubplot(plotNum, result, array, data_label, mean, std) + self.fig.subplots_adjust(hspace=0.5,wspace=0.5) self.canvas.draw() - return subplot + + def getMeanArrayAndStdArray(self): + print(self.mean_array, self.std_array) + #return self.result_data_frame class BulkRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run @@ -133,7 +136,7 @@ class BulkRunResultsWidget(BaseResultsWidget): def __init__(self, parent, result_data_frame): super().__init__(parent) - + self.result_data_frame = result_data_frame self.grid_layout = QtWidgets.QGridLayout() self.grid_layout.setAlignment(Qt.AlignTop) self.layout.addLayout(self.grid_layout,0) @@ -143,12 +146,12 @@ def __init__(self, parent, result_data_frame): self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() self.addWidgetstoGridLayout(single_result) - self.button.clicked.connect(partial(self.addHistogramsToLayout, result_data_frame)) + self.button.clicked.connect(partial(self.addHistogramsToLayout)) scroll_area_widget = NoBorderScrollArea(self.canvas) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.layout.addWidget(scroll_area_widget,1) - self.addHistogramsToLayout(result_data_frame) + self.addHistogramsToLayout() def addWidgetstoGridLayout(self, result): widgetno=0 @@ -228,7 +231,8 @@ def showSecondParam(self): newList = [] self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) - def addHistogramsToLayout(self, result_data_frame): + def addHistogramsToLayout(self): + """And stores mean and std""" self.fig.clf() param_index = self.param_list_widget.currentIndex() @@ -238,7 +242,7 @@ def addHistogramsToLayout(self, result_data_frame): numColumns = len(self.subvol_points) plotNum = 0 - for row in result_data_frame.itertuples(): + for row in self.result_data_frame.itertuples(): result = row.result if param_index == 1: @@ -251,15 +255,18 @@ def addHistogramsToLayout(self, result_data_frame): continue data_label = f"{self.data_label_widget.currentText()}" data_index = self.data_label_widget.currentIndex() + mean = row.mean_array[data_index] + std = row.std_array[data_index] plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label) + self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label, mean, std) subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) - self.fig.subplots_adjust(hspace=2,wspace=0.5) self.canvas.draw() + + class SaveObjectWindow(QtWidgets.QWidget): '''a window which will appear when saving a mask or pointcloud diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 3bfd7cd6..9a9e4209 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -22,7 +22,7 @@ import logging import json import pandas as pd -from idvc.utils.manipulate_result_files import createResultsDataFrame +from idvc.utils.manipulate_result_files import createResultsDataFrame, addMeanAndStdToResultDataFrame class VisualisationWindow(QtWidgets.QMainWindow): @@ -231,9 +231,12 @@ def DeleteAllWidgets(self): def CreateDockWidgets(self, displ_wrt_point0 = False): result_data_frame = createResultsDataFrame(self.results_folder, displ_wrt_point0) - print(result_data_frame) - for result in result_data_frame['result']: - single_run_results_widget = SingleRunResultsWidget(self, result, displ_wrt_point0) + result_data_frame = addMeanAndStdToResultDataFrame(result_data_frame) + for row in result_data_frame.itertuples(): + result = row.result + mean_array = row.mean_array + std_array = row.std_array + single_run_results_widget = SingleRunResultsWidget(self, result, displ_wrt_point0, mean_array, std_array) dock1 = QDockWidget(result.title,self) dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) @@ -252,14 +255,23 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): if len(result_data_frame) > 1: bulk_run_results_widget = BulkRunResultsWidget(self, result_data_frame) - dock = QDockWidget("Bulk",self) - dock.setFeatures(QDockWidget.NoDockWidgetFeatures) - dock.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - dock.setWidget(bulk_run_results_widget) - self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock) - self.tabifyDockWidget(prev,dock) - - dock.raise_() # makes bulk panel the one that is open by default. + dock2 = QDockWidget("Bulk",self) + dock2.setFeatures(QDockWidget.NoDockWidgetFeatures) + dock2.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) + dock2.setWidget(bulk_run_results_widget) + self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock2) + self.tabifyDockWidget(prev,dock2) + + dock2.raise_() # makes bulk panel the one that is open by default. + + # add statistial analysis tab + statistical_analisis_widget = QWidget() + dock3 = QDockWidget("Statistical analysis",self) + dock3.setFeatures(QDockWidget.NoDockWidgetFeatures) + dock3.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) + dock3.setWidget(statistical_analisis_widget) + self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock3) + self.tabifyDockWidget(dock2,dock3) # Stop the widgets in the tab to be moved around for wdg in self.findChildren(QTabBar): diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py index aade3c2b..a4f6773b 100644 --- a/src/idvc/utils/manipulate_result_files.py +++ b/src/idvc/utils/manipulate_result_files.py @@ -42,4 +42,19 @@ def createResultsDataFrame(results_folder, displ_wrt_point0): 'subvol_points': subvol_points_list, 'result': result_list, 'result_arrays': result_arrays_list}) + return result_data_frame + +def addMeanAndStdToResultDataFrame(result_data_frame): + mean_array_list = [] + std_array_list = [] + for row in result_data_frame.itertuples(): + mean_array = [] + std_array = [] + for array in row.result_arrays: + mean_array.append(array.mean()) + std_array.append(array.std()) + mean_array_list.append(mean_array) + std_array_list.append(std_array) + result_data_frame['mean_array'] = mean_array_list + result_data_frame['std_array'] = std_array_list return result_data_frame \ No newline at end of file From 4abb11960a4dcabf11f634e3bcf853ac348643db Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 18 Sep 2024 10:57:01 +0100 Subject: [PATCH 11/39] Add attempt for stat tab --- src/idvc/ui/widgets.py | 41 +++++++++++++++++++++++++++++++++++++++++ src/idvc/ui/windows.py | 4 +++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 7e191c3d..946d774c 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -72,6 +72,47 @@ def addSubplot(self, plotNum, result, array, data_label, mean, std): plt.legend(loc='upper right') + def addStatisticalAnalysisPlot(self, data_label,xpoints,ypoints): + xlabel = data_label + # Create the plot + plt.plot(xpoints, ypoints) + plt.ylabel("Mean") + plt.xlabel(xlabel) + + + + + def initTab(self, result_data_frame): + self.subvol_sizes = result_data_frame['subvol_size'].unique() + self.subvol_points = result_data_frame['subvol_points'].unique() + + def addManyPlots(self, data_type, result_data_frame): + df = result_data_frame + + + numRows = len(self.subvol_sizes) + numColumns = len(self.subvol_points) + plotNum = 0 + + self.fig.subplots_adjust(hspace=0.5,wspace=0.5) + + self.canvas.draw() + + if data_type == 'subvol_size': + df_sz_list = [] + for subvol_size in self.subvol_sizes: + self.fig.suptitle(f"Subvolume size: {subvol_size}",fontsize='xx-large') + data_index = 0 + plotNum = plotNum + 1 + self.fig.add_subplot(numRows, numColumns, plotNum) + df_sz = df[(df['subvol_size'] == subvol_size)] + + xpoints = df_sz['subvol_points'] + ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot("Objective minimum",xpoints,ypoints) + df_sz_list.append(df_sz) + + class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 9a9e4209..7d1b2167 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -265,7 +265,9 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): dock2.raise_() # makes bulk panel the one that is open by default. # add statistial analysis tab - statistical_analisis_widget = QWidget() + statistical_analisis_widget = BaseResultsWidget(self) + statistical_analisis_widget.initTab(result_data_frame) + statistical_analisis_widget.addManyPlots('subvol_size',result_data_frame) dock3 = QDockWidget("Statistical analysis",self) dock3.setFeatures(QDockWidget.NoDockWidgetFeatures) dock3.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) From 942ac9202916a5504d985198d52f4a657426fffe Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 18 Sep 2024 15:44:51 +0100 Subject: [PATCH 12/39] Add mean and std plots for objective min --- src/idvc/ui/widgets.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 946d774c..2ed6cb1a 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -72,16 +72,12 @@ def addSubplot(self, plotNum, result, array, data_label, mean, std): plt.legend(loc='upper right') - def addStatisticalAnalysisPlot(self, data_label,xpoints,ypoints): - xlabel = data_label + def addStatisticalAnalysisPlot(self, xlabel, ylabel, xpoints,ypoints, color): # Create the plot - plt.plot(xpoints, ypoints) - plt.ylabel("Mean") + plt.plot(xpoints, ypoints, color+'-') + plt.ylabel(ylabel) plt.xlabel(xlabel) - - - def initTab(self, result_data_frame): self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() @@ -91,7 +87,7 @@ def addManyPlots(self, data_type, result_data_frame): numRows = len(self.subvol_sizes) - numColumns = len(self.subvol_points) + numColumns = 2 plotNum = 0 self.fig.subplots_adjust(hspace=0.5,wspace=0.5) @@ -99,18 +95,26 @@ def addManyPlots(self, data_type, result_data_frame): self.canvas.draw() if data_type == 'subvol_size': - df_sz_list = [] + + self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') for subvol_size in self.subvol_sizes: - self.fig.suptitle(f"Subvolume size: {subvol_size}",fontsize='xx-large') data_index = 0 - plotNum = plotNum + 1 - self.fig.add_subplot(numRows, numColumns, plotNum) - df_sz = df[(df['subvol_size'] == subvol_size)] + df_sz = df[(df['subvol_size'] == subvol_size)] xpoints = df_sz['subvol_points'] + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot("Objective minimum",xpoints,ypoints) - df_sz_list.append(df_sz) + self.addStatisticalAnalysisPlot("Points in subvolume", "Objective minimum mean",xpoints,ypoints, 'r') + subplot.set_title(f"Subvolume size: {subvol_size}", fontsize='x-large', pad=20) + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot("Points in subvolume", "Objective minimum std", xpoints,ypoints, 'g') + subplot.set_title(f"Subvolume size: {subvol_size}", fontsize='x-large', pad=20) + class SingleRunResultsWidget(BaseResultsWidget): From f58b5013de59166c153f1e8dcf147da4ce44ca3b Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 18 Sep 2024 18:05:47 +0100 Subject: [PATCH 13/39] Add statistical analysis on the subpoints and sub size, with the new class. Problem with the bulk tab needs to be solved --- src/idvc/ui/widgets.py | 147 +++++++++++++++++++++++++---------------- src/idvc/ui/windows.py | 4 +- 2 files changed, 91 insertions(+), 60 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 2ed6cb1a..203f85f6 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -43,9 +43,6 @@ def __init__(self, parent): self.layout.addWidget(self.canvas) self.setLayout(self.layout) - - - def addSubplot(self, plotNum, result, array, data_label, mean, std): '''plot the Gaussian curve, legend @@ -72,50 +69,11 @@ def addSubplot(self, plotNum, result, array, data_label, mean, std): plt.legend(loc='upper right') - def addStatisticalAnalysisPlot(self, xlabel, ylabel, xpoints,ypoints, color): + def addStatisticalAnalysisPlot(self, xlabel, ylabel, xpoints, ypoints, color): # Create the plot plt.plot(xpoints, ypoints, color+'-') plt.ylabel(ylabel) plt.xlabel(xlabel) - - def initTab(self, result_data_frame): - self.subvol_sizes = result_data_frame['subvol_size'].unique() - self.subvol_points = result_data_frame['subvol_points'].unique() - - def addManyPlots(self, data_type, result_data_frame): - df = result_data_frame - - - numRows = len(self.subvol_sizes) - numColumns = 2 - plotNum = 0 - - self.fig.subplots_adjust(hspace=0.5,wspace=0.5) - - self.canvas.draw() - - if data_type == 'subvol_size': - - self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') - for subvol_size in self.subvol_sizes: - data_index = 0 - - df_sz = df[(df['subvol_size'] == subvol_size)] - xpoints = df_sz['subvol_points'] - - plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot("Points in subvolume", "Objective minimum mean",xpoints,ypoints, 'r') - subplot.set_title(f"Subvolume size: {subvol_size}", fontsize='x-large', pad=20) - - plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot("Points in subvolume", "Objective minimum std", xpoints,ypoints, 'g') - subplot.set_title(f"Subvolume size: {subvol_size}", fontsize='x-large', pad=20) - - class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code @@ -167,19 +125,17 @@ def addHistogramsToLayout(self, result, displ_wrt_point0, mean_array, std_array) self.addSubplot(plotNum, result, array, data_label, mean, std) - self.fig.subplots_adjust(hspace=0.5,wspace=0.5) + #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) self.canvas.draw() - - def getMeanArrayAndStdArray(self): - print(self.mean_array, self.std_array) - #return self.result_data_frame -class BulkRunResultsWidget(BaseResultsWidget): + +class BulkRunResultsBaseWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' def __init__(self, parent, result_data_frame): super().__init__(parent) + print("init BulkRunResultsBaseWidget") self.result_data_frame = result_data_frame self.grid_layout = QtWidgets.QGridLayout() @@ -191,12 +147,12 @@ def __init__(self, parent, result_data_frame): self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() self.addWidgetstoGridLayout(single_result) - self.button.clicked.connect(partial(self.addHistogramsToLayout)) + self.button.clicked.connect(partial(self.addPlotsToLayout)) scroll_area_widget = NoBorderScrollArea(self.canvas) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self.layout.addWidget(scroll_area_widget,1) - self.addHistogramsToLayout() + self.layout.addWidget(scroll_area_widget,1) + self.addPlotsToLayout() def addWidgetstoGridLayout(self, result): widgetno=0 @@ -275,9 +231,19 @@ def showSecondParam(self): self.secondParamCombo.clear() newList = [] self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) - - def addHistogramsToLayout(self): - """And stores mean and std""" + + def addPlotsToLayout(self): + print("addplot0") + pass +class BulkRunResultsWidget(BulkRunResultsBaseWidget): + def __init__(self, parent, result_data_frame): + print("init2") + super().__init__(parent, result_data_frame) + + + def addPlotsToLayout(self): + """And stores mean and std"""# + print("addplotb") self.fig.clf() param_index = self.param_list_widget.currentIndex() @@ -306,11 +272,78 @@ def addHistogramsToLayout(self): subplot = self.fig.add_subplot(numRows, numColumns, plotNum) self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label, mean, std) subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) - self.fig.subplots_adjust(hspace=2,wspace=0.5) + #self.fig.subplots_adjust(hspace=2,wspace=0.5) self.canvas.draw() +class StatisticsResultsWidget(BulkRunResultsBaseWidget): + def __init__(self, parent, result_data_frame): + print("init 3") + super().__init__(parent, result_data_frame) - + def addPlotsToLayout(self): + print("addplots") + self.fig.clf() + df = self.result_data_frame + param_index = self.param_list_widget.currentIndex() + + + + numColumns = 2 + plotNum = 0 + self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') + if param_index == 0: + pass + else: + if param_index == 1: + data_type = 'subvol_points' + other_type ='subvol_size' + numRows = len(self.subvol_sizes) + for subvol_size in self.subvol_sizes: + if subvol_size != float(self.secondParamCombo.currentText()): + pass#continue + data_label = f"{self.data_label_widget.currentText()}" + data_index = self.data_label_widget.currentIndex() + df_sz = df[(df[other_type] == subvol_size)] + xpoints = df_sz[data_type] + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot(f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') + subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot(f"{data_type}", data_label + " std", xpoints,ypoints, 'g') + subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) + + #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) + elif param_index == 2: + data_type = 'subvol_size' + other_type = 'subvol_points' + numRows = len(self.subvol_points) + for subvol_points in self.subvol_points: + if subvol_points != float(self.secondParamCombo.currentText()): + pass#continue + data_label = f"{self.data_label_widget.currentText()}" + data_index = self.data_label_widget.currentIndex() + df_sz = df[(df[other_type] == subvol_points)] + xpoints = df_sz[data_type] + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot(f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') + subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) + + plotNum = plotNum + 1 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot(f"{data_type}", data_label + " std", xpoints,ypoints, 'g') + subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) + + self.canvas.draw() class SaveObjectWindow(QtWidgets.QWidget): diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 7d1b2167..a5157c85 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -265,9 +265,7 @@ def CreateDockWidgets(self, displ_wrt_point0 = False): dock2.raise_() # makes bulk panel the one that is open by default. # add statistial analysis tab - statistical_analisis_widget = BaseResultsWidget(self) - statistical_analisis_widget.initTab(result_data_frame) - statistical_analisis_widget.addManyPlots('subvol_size',result_data_frame) + statistical_analisis_widget = StatisticsResultsWidget(self, result_data_frame) dock3 = QDockWidget("Statistical analysis",self) dock3.setFeatures(QDockWidget.NoDockWidgetFeatures) dock3.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) From 4e009c3b9e962b01b82d24f3af095150c4504c13 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 23 Sep 2024 14:00:31 +0100 Subject: [PATCH 14/39] Remove plt and fix group plotting --- src/idvc/ui/widgets.py | 181 +++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 88 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 203f85f6..04a060ad 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -31,10 +31,8 @@ def __init__(self, parent): results: RunResults displ_wrt_point0: bool ''' - super().__init__() - self.parent = parent - self.fig = Figure() - self.fig, hist_plot = plt.subplots(figsize=(10, 8), layout="constrained") + super().__init__(parent = parent) + self.fig = Figure(figsize=(10, 8)) self.canvas = FigureCanvas(self.fig) self.fig.clf() self.canvas.setMinimumSize(400, 400) #needed for scrollbar @@ -43,7 +41,7 @@ def __init__(self, parent): self.layout.addWidget(self.canvas) self.setLayout(self.layout) - def addSubplot(self, plotNum, result, array, data_label, mean, std): + def addSubplot(self, plot, array, xlabel, mean, std): '''plot the Gaussian curve, legend Returns @@ -51,29 +49,28 @@ def addSubplot(self, plotNum, result, array, data_label, mean, std): matplotlib.pyplot A plot of the histogram """''' - xlabel = data_label - counts, bins, patches = plt.hist(array, bins=20) + counts, bins = plot.hist(array, bins=20)[0:2] relative_counts = counts*100/ len(array) - plt.cla() + plot.cla() bin_widths = np.diff(bins) - plt.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') - plt.ylabel("Relative frequency (% points in run)") - plt.xlabel(xlabel) - plt.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.3f}') - plt.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.3f}') - plt.axvline(mean+std, color='g', linestyle='--') + plot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') + plot.set_ylabel("Relative frequency (% points in run)") + plot.set_xlabel(xlabel) + plot.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.3f}') + plot.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.3f}') + plot.axvline(mean+std, color='g', linestyle='--') x = np.linspace(min(array), max(array), 1000) gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 - plt.plot(x, gaussian, 'b--', label='gaussian fit') + plot.plot(x, gaussian, 'b--', label='gaussian fit') - plt.legend(loc='upper right') + plot.legend(loc='upper right') - def addStatisticalAnalysisPlot(self, xlabel, ylabel, xpoints, ypoints, color): + def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color): # Create the plot - plt.plot(xpoints, ypoints, color+'-') - plt.ylabel(ylabel) - plt.xlabel(xlabel) + subplot.plot(xpoints, ypoints, color+'-') + subplot.set_ylabel(ylabel) + subplot.set_xlabel(xlabel) class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code @@ -113,19 +110,15 @@ def addHistogramsToLayout(self, result, displ_wrt_point0, mean_array, std_array) numRows = int(np.round(np.sqrt(numGraphs))) numColumns = int(np.ceil(numGraphs/numRows)) - - - plotNum = 0 - for array in result_arrays: + for plotNum, array in enumerate(result_arrays): data_label = result.data_label[plotNum] mean = mean_array[plotNum] std = std_array[plotNum] - plotNum = plotNum + 1 - self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(plotNum, result, array, data_label, mean, std) + subplot = self.fig.add_subplot(numRows, numColumns, plotNum + 1) + self.addSubplot(subplot, array, data_label, mean, std) - #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) + self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() @@ -185,8 +178,8 @@ def addWidgetstoGridLayout(self, result): self.grid_layout.addWidget(self.label1,widgetno,1) self.param_list_widget = QComboBox(self) - self.param_list = ["All","Sampling points in subvolume", "Subvolume size"] self.param_list_widget.addItems(self.param_list) + self.grid_layout.addWidget(self.param_list_widget,widgetno,2) widgetno+=1 @@ -195,13 +188,14 @@ def addWidgetstoGridLayout(self, result): self.grid_layout.addWidget(self.secondParamLabel,widgetno,1) self.secondParamCombo = QComboBox(self) - self.secondParamCombo.addItems(self.subvol_sizes) + self.secondParamCombo_subvol_sizes = self.subvol_sizes + self.secondParamCombo_subvol_points = self.subvol_points + self.secondParamCombo.addItems(self.secondParamCombo_subvol_sizes) self.grid_layout.addWidget(self.secondParamCombo,widgetno,2) widgetno+=1 self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) - self.secondParamLabel.hide() - self.secondParamCombo.hide() + self.showSecondParam() self.button = QtWidgets.QPushButton("Plot Histograms") @@ -213,34 +207,35 @@ def addWidgetstoGridLayout(self, result): def showSecondParam(self): index = self.param_list_widget.currentIndex() - if index ==0: - self.secondParamLabel.hide() - self.secondParamCombo.hide() - elif index == 1: + if index == 0: self.secondParamLabel.show() self.secondParamCombo.show() self.secondParamLabel.setText("Subvolume Size:") self.secondParamCombo.clear() - self.secondParamCombo.addItems([str(i) for i in self.subvol_sizes]) + self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_sizes]) - elif index == 2: + elif index == 1: self.secondParamLabel.show() self.secondParamCombo.show() self.secondParamLabel.setText("Points in Subvolume:") self.secondParamCombo.clear() - newList = [] - self.secondParamCombo.addItems([str(i) for i in self.subvol_points]) + self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_points]) + + elif index ==2: + self.secondParamLabel.hide() + self.secondParamCombo.hide() def addPlotsToLayout(self): print("addplot0") pass + class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): print("init2") + self.param_list = ["Sampling points in subvolume", "Subvolume size", "All"] super().__init__(parent, result_data_frame) - - + def addPlotsToLayout(self): """And stores mean and std"""# print("addplotb") @@ -256,11 +251,11 @@ def addPlotsToLayout(self): for row in self.result_data_frame.itertuples(): result = row.result - if param_index == 1: + if param_index == 0: numRows = 1 if result.subvol_size != float(self.secondParamCombo.currentText()): continue - elif param_index ==2: + elif param_index == 1: numColumns = 1 if result.subvol_points != float(self.secondParamCombo.currentText()): continue @@ -270,15 +265,20 @@ def addPlotsToLayout(self): std = row.std_array[data_index] plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(plotNum, result, row.result_arrays[data_index], data_label, mean, std) + self.addSubplot(subplot, row.result_arrays[data_index], data_label, mean, std) subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) - #self.fig.subplots_adjust(hspace=2,wspace=0.5) + self.fig.subplots_adjust(hspace=2,wspace=0.5) + self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() class StatisticsResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): print("init 3") + self.param_list = ["Sampling points in subvolume", "Subvolume size"] super().__init__(parent, result_data_frame) + self.secondParamCombo_subvol_sizes = np.append(self.secondParamCombo_subvol_sizes, "All") + self.secondParamCombo_subvol_points = np.append(self.secondParamCombo_subvol_points, "All") + self.secondParamCombo.addItems(["All"]) def addPlotsToLayout(self): print("addplots") @@ -291,16 +291,19 @@ def addPlotsToLayout(self): numColumns = 2 plotNum = 0 self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') - if param_index == 0: + if param_index == 2: pass else: - if param_index == 1: + if param_index == 0: data_type = 'subvol_points' other_type ='subvol_size' numRows = len(self.subvol_sizes) for subvol_size in self.subvol_sizes: - if subvol_size != float(self.secondParamCombo.currentText()): - pass#continue + if str(subvol_size) != self.secondParamCombo.currentText(): + if self.secondParamCombo.currentText() == "All": + pass + else: + continue data_label = f"{self.data_label_widget.currentText()}" data_index = self.data_label_widget.currentIndex() df_sz = df[(df[other_type] == subvol_size)] @@ -309,23 +312,26 @@ def addPlotsToLayout(self): plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') + self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(f"{data_type}", data_label + " std", xpoints,ypoints, 'g') + self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label + " std", xpoints,ypoints, 'g') subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) - elif param_index == 2: + elif param_index == 1: data_type = 'subvol_size' other_type = 'subvol_points' numRows = len(self.subvol_points) for subvol_points in self.subvol_points: - if subvol_points != float(self.secondParamCombo.currentText()): - pass#continue + if str(subvol_points) != self.secondParamCombo.currentText(): + if self.secondParamCombo.currentText() == "All": + pass + else: + continue data_label = f"{self.data_label_widget.currentText()}" data_index = self.data_label_widget.currentIndex() df_sz = df[(df[other_type] == subvol_points)] @@ -334,15 +340,16 @@ def addPlotsToLayout(self): plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') + self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(f"{data_type}", data_label + " std", xpoints,ypoints, 'g') + self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label + " std", xpoints,ypoints, 'g') subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) - + + self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() @@ -352,11 +359,9 @@ class SaveObjectWindow(QtWidgets.QWidget): #self.copy_files_label = QLabel("Allow a copy of the image files to be stored: ") def __init__(self, parent, object_type, save_only): - super().__init__() + super().__init__(parent = parent) #print(save_only) - - self.parent = parent self.object = object_type if self.object == "mask": @@ -395,37 +400,37 @@ def save(self, save_only): #Load Saved Session #print("Write mask to file, then carry on") filename = self.textbox.text() + ".mha" - shutil.copyfile(os.path.join(tempfile.tempdir, self.parent.mask_file), os.path.join(tempfile.tempdir, "Masks", filename)) - self.parent.mask_parameters['masksList'].addItem(filename) - self.parent.mask_details[filename] = self.parent.mask_details['current'] - #print(self.parent.mask_details) + shutil.copyfile(os.path.join(tempfile.tempdir, self.parent().mask_file), os.path.join(tempfile.tempdir, "Masks", filename)) + self.parent().mask_parameters['masksList'].addItem(filename) + self.parent().mask_details[filename] = self.parent().mask_details['current'] + #print(self.parent().mask_details) - self.parent.mask_parameters['loadButton'].setEnabled(True) - self.parent.mask_parameters['masksList'].setEnabled(True) + self.parent().mask_parameters['loadButton'].setEnabled(True) + self.parent().mask_parameters['masksList'].setEnabled(True) if not save_only: #print("Not save only") #would be better to move this elsewhere - self.parent.mask_worker = Worker(self.parent.extendMask) - self.parent.create_progress_window("Loading", "Loading Mask") - self.parent.mask_worker.signals.progress.connect(self.parent.progress) - self.parent.mask_worker.signals.finished.connect(self.parent.DisplayMask) - self.parent.threadpool.start(self.parent.mask_worker) - self.parent.progress_window.setValue(10) + self.parent().mask_worker = Worker(self.parent().extendMask) + self.parent().create_progress_window("Loading", "Loading Mask") + self.parent().mask_worker.signals.progress.connect(self.parent().progress) + self.parent().mask_worker.signals.finished.connect(self.parent().DisplayMask) + self.parent().threadpool.start(self.parent().mask_worker) + self.parent().progress_window.setValue(10) if self.object == "pointcloud": filename = self.textbox.text() + ".roi" shutil.copyfile(os.path.join(tempfile.tempdir, "latest_pointcloud.roi"), os.path.join(tempfile.tempdir, filename)) - self.parent.pointcloud_parameters['loadButton'].setEnabled(True) - self.parent.pointcloud_parameters['pointcloudList'].setEnabled(True) - self.parent.pointcloud_parameters['pointcloudList'].addItem(filename) - self.parent.pointCloud_details[filename] = self.parent.pointCloud_details['latest_pointcloud.roi'] - #print(self.parent.pointCloud_details) - #self.parent.createPointCloud() + self.parent().pointcloud_parameters['loadButton'].setEnabled(True) + self.parent().pointcloud_parameters['pointcloudList'].setEnabled(True) + self.parent().pointcloud_parameters['pointcloudList'].addItem(filename) + self.parent().pointCloud_details[filename] = self.parent().pointCloud_details['latest_pointcloud.roi'] + #print(self.parent().pointCloud_details) + #self.parent().createPointCloud() if not save_only: - self.parent.PointCloudWorker("create") + self.parent().PointCloudWorker("create") self.close() @@ -433,15 +438,15 @@ def save(self, save_only): def quit(self): if self.object == "mask": #would be better to move this elsewhere - self.parent.mask_worker = Worker(self.parent.extendMask) - self.parent.create_progress_window("Loading", "Loading Mask") - self.parent.mask_worker.signals.progress.connect(self.parent.progress) - self.parent.mask_worker.signals.finished.connect(self.parent.DisplayMask) - self.parent.threadpool.start(self.parent.mask_worker) - self.parent.progress_window.setValue(10) + self.parent().mask_worker = Worker(self.parent().extendMask) + self.parent().create_progress_window("Loading", "Loading Mask") + self.parent().mask_worker.signals.progress.connect(self.parent().progress) + self.parent().mask_worker.signals.finished.connect(self.parent().DisplayMask) + self.parent().threadpool.start(self.parent().mask_worker) + self.parent().progress_window.setValue(10) if self.object == "pointcloud": - self.parent.PointCloudWorker("create") - #self.parent.createPointCloud() + self.parent().PointCloudWorker("create") + #self.parent().createPointCloud() self.close() From 3af9cf85706702b63edfa5b648c61b3e163771ec Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 24 Sep 2024 10:24:58 +0100 Subject: [PATCH 15/39] Restructure parent class widgets --- src/idvc/ui/widgets.py | 113 +++++++++++++++++++++++------------------ src/idvc/ui/windows.py | 28 ++++------ 2 files changed, 73 insertions(+), 68 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 04a060ad..60e41a16 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -20,7 +20,7 @@ class BaseResultsWidget(QtWidgets.QWidget): '''creates a dockable widget which will display graph results from runs of the DVC code ''' - def __init__(self, parent): + def __init__(self, parent, result_data_frame): ''' Adds vertical layout. figure. @@ -31,17 +31,55 @@ def __init__(self, parent): results: RunResults displ_wrt_point0: bool ''' + self.result_data_frame = result_data_frame + single_result = result_data_frame.iloc[0]['result'] + self.run_name = single_result.run_name + self.subvol_sizes = result_data_frame['subvol_size'].unique() + self.subvol_points = result_data_frame['subvol_points'].unique() super().__init__(parent = parent) + self.layout = QtWidgets.QVBoxLayout() + self.setLayout(self.layout) + + self.grid_layout = QtWidgets.QGridLayout() + self.grid_layout.setAlignment(Qt.AlignTop) + self.layout.addLayout(self.grid_layout,0) + self.addInfotoGridLayout(single_result) + self.fig = Figure(figsize=(10, 8)) self.canvas = FigureCanvas(self.fig) self.fig.clf() self.canvas.setMinimumSize(400, 400) #needed for scrollbar + self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.toolbar = NavigationToolbar(self.canvas, self) - self.layout = QtWidgets.QVBoxLayout() - self.layout.addWidget(self.canvas) - self.setLayout(self.layout) + self.layout.addWidget(self.toolbar) + + scroll_area_widget = NoBorderScrollArea(self.canvas) + self.layout.addWidget(scroll_area_widget,1) + + + def addInfotoGridLayout(self, result): + widgetno=0 + self.results_details_label = QLabel(self) + self.results_details_label.setText("Subvolume Geometry: {subvol_geom}\n\ +Maximum Displacement: {disp_max}\n\ +Degrees of Freedom: {num_srch_dof}\n\ +Objective Function: {obj_function}\n\ +Interpolation Type: {interp_type}\n\ +Rigid Body Offset: {rigid_trans}".format(subvol_geom=result.subvol_geom, \ + disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ + interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) + self.grid_layout.addWidget(self.results_details_label,widgetno,0,5,1) + self.results_details_label.setAlignment(Qt.AlignTop) + + def addPlotsToLayout(self): + pass + + def addWidgetsToGridLayout(self, result): + print("parent grid") + pass - def addSubplot(self, plot, array, xlabel, mean, std): + def addHistogramSubplot(self, plot, array, xlabel, mean, std): '''plot the Gaussian curve, legend Returns @@ -75,21 +113,23 @@ def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent, result, displ_wrt_point0, mean_array, std_array): + def __init__(self, parent, result_data_frame, displ_wrt_point0, mean_array, std_array): ''' Parameters ---------- results: RunResults displ_wrt_point0: bool ''' - super().__init__(parent) - self.layout.addWidget(self.toolbar) - self.layout.addWidget(self.canvas, stretch=1) - self.setLayout(self.layout) - self.addHistogramsToLayout(result, displ_wrt_point0, mean_array, std_array) - + super().__init__(parent, result_data_frame) + single_result = result_data_frame.iloc[0]['result'] + self.addWidgetsToGridLayout(single_result) + self.addPlotsToLayout(single_result, displ_wrt_point0, mean_array, std_array) + + def addWidgetsToGridLayout(self, result): + print("single grid") + pass - def addHistogramsToLayout(self, result, displ_wrt_point0, mean_array, std_array): + def addPlotsToLayout(self, result, displ_wrt_point0, mean_array, std_array): ''' Extracts the data from the disp file. @@ -115,7 +155,7 @@ def addHistogramsToLayout(self, result, displ_wrt_point0, mean_array, std_array) mean = mean_array[plotNum] std = std_array[plotNum] subplot = self.fig.add_subplot(numRows, numColumns, plotNum + 1) - self.addSubplot(subplot, array, data_label, mean, std) + self.addHistogramSubplot(subplot, array, data_label, mean, std) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) @@ -127,42 +167,13 @@ class BulkRunResultsBaseWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' def __init__(self, parent, result_data_frame): - super().__init__(parent) - print("init BulkRunResultsBaseWidget") - - self.result_data_frame = result_data_frame - self.grid_layout = QtWidgets.QGridLayout() - self.grid_layout.setAlignment(Qt.AlignTop) - self.layout.addLayout(self.grid_layout,0) - + super().__init__(parent, result_data_frame) single_result = result_data_frame.iloc[0]['result'] - self.run_name = single_result.run_name - self.subvol_sizes = result_data_frame['subvol_size'].unique() - self.subvol_points = result_data_frame['subvol_points'].unique() self.addWidgetstoGridLayout(single_result) - - self.button.clicked.connect(partial(self.addPlotsToLayout)) - scroll_area_widget = NoBorderScrollArea(self.canvas) - self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self.layout.addWidget(scroll_area_widget,1) - self.addPlotsToLayout() def addWidgetstoGridLayout(self, result): - widgetno=0 - - self.results_details_label = QLabel(self) - self.results_details_label.setText("Subvolume Geometry: {subvol_geom}\n\ -Maximum Displacement: {disp_max}\n\ -Degrees of Freedom: {num_srch_dof}\n\ -Objective Function: {obj_function}\n\ -Interpolation Type: {interp_type}\n\ -Rigid Body Offset: {rigid_trans}".format(subvol_geom=result.subvol_geom, \ - disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ - interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) - self.grid_layout.addWidget(self.results_details_label,widgetno,0,5,1) - self.results_details_label.setAlignment(Qt.AlignTop) - widgetno+=1 - + print("second grid") + widgetno=1 self.label = QLabel(self) self.label.setText("Select data: ") @@ -198,6 +209,7 @@ def addWidgetstoGridLayout(self, result): self.showSecondParam() self.button = QtWidgets.QPushButton("Plot Histograms") + self.button.clicked.connect(partial(self.addPlotsToLayout)) self.grid_layout.addWidget(self.button,widgetno,2) widgetno+=1 @@ -226,9 +238,7 @@ def showSecondParam(self): self.secondParamLabel.hide() self.secondParamCombo.hide() - def addPlotsToLayout(self): - print("addplot0") - pass + class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): @@ -265,7 +275,7 @@ def addPlotsToLayout(self): std = row.std_array[data_index] plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - self.addSubplot(subplot, row.result_arrays[data_index], data_label, mean, std) + self.addHistogramSubplot(subplot, row.result_arrays[data_index], data_label, mean, std) subplot.set_title(f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}", fontsize='x-large', pad=20) self.fig.subplots_adjust(hspace=2,wspace=0.5) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) @@ -276,6 +286,9 @@ def __init__(self, parent, result_data_frame): print("init 3") self.param_list = ["Sampling points in subvolume", "Subvolume size"] super().__init__(parent, result_data_frame) + + self.secondParamCombo_subvol_sizes = self.subvol_sizes + self.secondParamCombo_subvol_points = self.subvol_points self.secondParamCombo_subvol_sizes = np.append(self.secondParamCombo_subvol_sizes, "All") self.secondParamCombo_subvol_points = np.append(self.secondParamCombo_subvol_points, "All") self.secondParamCombo.addItems(["All"]) diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index a5157c85..6468b12f 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -188,7 +188,6 @@ def __init__(self, parent=None): # Menu self.menu = self.menuBar() - self.file_menu = self.menu.addMenu("File") self.settings_menu = self.menu.addMenu("Settings") displacement_setting_action = QAction("Show Displacement Relative to Reference Point 0", self) @@ -199,13 +198,6 @@ def __init__(self, parent=None): displacement_setting_action.triggered.connect(self.ReloadGraphs) self.settings_menu.addAction(displacement_setting_action) - - # Exit QAction - exit_action = QAction("Exit", self) - exit_action.setShortcut(QKeySequence.Quit) - exit_action.triggered.connect(self.close) - self.file_menu.addAction(exit_action) - #Tab positions: self.setTabPosition(QtCore.Qt.AllDockWidgetAreas,QTabWidget.North) self.setDockOptions(QMainWindow.ForceTabbedDocks) @@ -232,16 +224,16 @@ def DeleteAllWidgets(self): def CreateDockWidgets(self, displ_wrt_point0 = False): result_data_frame = createResultsDataFrame(self.results_folder, displ_wrt_point0) result_data_frame = addMeanAndStdToResultDataFrame(result_data_frame) - for row in result_data_frame.itertuples(): - result = row.result - mean_array = row.mean_array - std_array = row.std_array - single_run_results_widget = SingleRunResultsWidget(self, result, displ_wrt_point0, mean_array, std_array) - dock1 = QDockWidget(result.title,self) - dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) - dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) - dock1.setWidget(single_run_results_widget) - self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock1) + row = result_data_frame.iloc[0] + result = row.result + mean_array = row.mean_array + std_array = row.std_array + single_run_results_widget = SingleRunResultsWidget(self, result_data_frame, displ_wrt_point0, mean_array, std_array) + dock1 = QDockWidget(result.title,self) + dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) + dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) + dock1.setWidget(single_run_results_widget) + self.addDockWidget(QtCore.Qt.RightDockWidgetArea,dock1) prev = None From 21e38b3da081171e14730cedff745e9b69483fb8 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 24 Sep 2024 11:26:12 +0100 Subject: [PATCH 16/39] Edit widgets and methods --- src/idvc/ui/widgets.py | 59 ++++++++++++++++++++++++++++++------------ src/idvc/ui/windows.py | 2 +- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 60e41a16..efba356f 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -122,12 +122,38 @@ def __init__(self, parent, result_data_frame, displ_wrt_point0, mean_array, std_ ''' super().__init__(parent, result_data_frame) single_result = result_data_frame.iloc[0]['result'] - self.addWidgetsToGridLayout(single_result) + if len(result_data_frame) > 1: + self.addWidgetsToGridLayout(single_result) self.addPlotsToLayout(single_result, displ_wrt_point0, mean_array, std_array) def addWidgetsToGridLayout(self, result): - print("single grid") - pass + self.secondParamCombo_subvol_sizes = self.subvol_sizes + self.secondParamCombo_subvol_points = self.subvol_points + + widgetno=1 + + self.subvol_points_label = QLabel(self) + self.subvol_points_label.setText("Select points in subvolume: ") + self.grid_layout.addWidget(self.subvol_points_label,widgetno,1) + + self.subvol_points_widget = QComboBox(self) + self.subvol_points_widget.addItems(self.secondParamCombo_subvol_points) + self.grid_layout.addWidget(self.subvol_points_widget,widgetno,2) + widgetno+=1 + + self.subvol_size_label = QLabel(self) + self.subvol_size_label.setText("Select subvolume size: ") + self.grid_layout.addWidget(self.subvol_size_label,widgetno,1) + + self.subvol_size_widget = QComboBox(self) + self.subvol_size_widget.addItems(self.secondParamCombo_subvol_sizes) + self.grid_layout.addWidget(self.subvol_size_widget,widgetno,2) + widgetno+=1 + + self.button = QtWidgets.QPushButton("Plot histograms") + self.button.clicked.connect(partial(self.addPlotsToLayout)) + self.grid_layout.addWidget(self.button,widgetno,2) + widgetno+=1 def addPlotsToLayout(self, result, displ_wrt_point0, mean_array, std_array): ''' @@ -166,17 +192,18 @@ def addPlotsToLayout(self, result, displ_wrt_point0, mean_array, std_array): class BulkRunResultsBaseWidget(BaseResultsWidget): '''creates a dockable widget which will display results from all runs in a bulk run ''' - def __init__(self, parent, result_data_frame): + def __init__(self, parent, result_data_frame, param_list, button_text = "Plot"): super().__init__(parent, result_data_frame) single_result = result_data_frame.iloc[0]['result'] - self.addWidgetstoGridLayout(single_result) + self.addWidgetstoGridLayout(single_result, param_list, button_text) + self.addPlotsToLayout() - def addWidgetstoGridLayout(self, result): + def addWidgetstoGridLayout(self, result, param_list, button_text): print("second grid") widgetno=1 self.label = QLabel(self) - self.label.setText("Select data: ") + self.label.setText("Select result to plot: ") self.grid_layout.addWidget(self.label,widgetno,1) self.data_label_widget = QComboBox(self) @@ -185,11 +212,11 @@ def addWidgetstoGridLayout(self, result): widgetno+=1 self.label1 = QLabel(self) - self.label1.setText("Select parameter: ") + self.label1.setText("Select parameter to fix: ") self.grid_layout.addWidget(self.label1,widgetno,1) self.param_list_widget = QComboBox(self) - self.param_list_widget.addItems(self.param_list) + self.param_list_widget.addItems(param_list) self.grid_layout.addWidget(self.param_list_widget,widgetno,2) widgetno+=1 @@ -208,15 +235,12 @@ def addWidgetstoGridLayout(self, result): self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) self.showSecondParam() - self.button = QtWidgets.QPushButton("Plot Histograms") + self.button = QtWidgets.QPushButton(button_text) self.button.clicked.connect(partial(self.addPlotsToLayout)) self.grid_layout.addWidget(self.button,widgetno,2) widgetno+=1 - self.grid_layout.addWidget(self.toolbar,widgetno,0,1,3) - widgetno+=1 - def showSecondParam(self): index = self.param_list_widget.currentIndex() @@ -243,8 +267,9 @@ def showSecondParam(self): class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): print("init2") - self.param_list = ["Sampling points in subvolume", "Subvolume size", "All"] - super().__init__(parent, result_data_frame) + param_list = ["Subvolume size", "Sampling points in subvolume", "None"] + super().__init__(parent, result_data_frame, param_list, "Plot histograms") + def addPlotsToLayout(self): """And stores mean and std"""# @@ -284,8 +309,8 @@ def addPlotsToLayout(self): class StatisticsResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): print("init 3") - self.param_list = ["Sampling points in subvolume", "Subvolume size"] - super().__init__(parent, result_data_frame) + param_list = ["Subvolume size", "Sampling points in subvolume"] + super().__init__(parent, result_data_frame, param_list) self.secondParamCombo_subvol_sizes = self.subvol_sizes self.secondParamCombo_subvol_points = self.subvol_points diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 6468b12f..9b98e482 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -188,7 +188,7 @@ def __init__(self, parent=None): # Menu self.menu = self.menuBar() - self.settings_menu = self.menu.addMenu("Settings") + self.settings_menu = self.menu.addMenu("Custom options") displacement_setting_action = QAction("Show Displacement Relative to Reference Point 0", self) displacement_setting_action.setCheckable(True) From 3404a1d12c8b27472860b7cd9dfb791379a0ad52 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 24 Sep 2024 17:01:32 +0100 Subject: [PATCH 17/39] Add option to select parameters in single tab --- src/idvc/ui/widgets.py | 57 ++++++++++++++++++++++++------------------ src/idvc/ui/windows.py | 8 ++---- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index efba356f..db4ebe4c 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -34,6 +34,7 @@ def __init__(self, parent, result_data_frame): self.result_data_frame = result_data_frame single_result = result_data_frame.iloc[0]['result'] self.run_name = single_result.run_name + self.data_label = single_result.data_label self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() super().__init__(parent = parent) @@ -57,7 +58,6 @@ def __init__(self, parent, result_data_frame): scroll_area_widget = NoBorderScrollArea(self.canvas) self.layout.addWidget(scroll_area_widget,1) - def addInfotoGridLayout(self, result): widgetno=0 self.results_details_label = QLabel(self) @@ -72,6 +72,15 @@ def addInfotoGridLayout(self, result): self.grid_layout.addWidget(self.results_details_label,widgetno,0,5,1) self.results_details_label.setAlignment(Qt.AlignTop) + def selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_size): + df = result_data_frame + if len(df) > 1: + df = df[(df['subvol_points'].astype(str) == selected_subvol_points) & (df['subvol_size'].astype(str) == selected_subvol_size)] + elif len(df) == 1: + df = self.result_data_frame + row = df.iloc[0] + return row + def addPlotsToLayout(self): pass @@ -110,10 +119,11 @@ def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, subplot.set_ylabel(ylabel) subplot.set_xlabel(xlabel) + class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' - def __init__(self, parent, result_data_frame, displ_wrt_point0, mean_array, std_array): + def __init__(self, parent, result_data_frame): ''' Parameters ---------- @@ -121,12 +131,11 @@ def __init__(self, parent, result_data_frame, displ_wrt_point0, mean_array, std_ displ_wrt_point0: bool ''' super().__init__(parent, result_data_frame) - single_result = result_data_frame.iloc[0]['result'] if len(result_data_frame) > 1: - self.addWidgetsToGridLayout(single_result) - self.addPlotsToLayout(single_result, displ_wrt_point0, mean_array, std_array) + self.addWidgetsToGridLayout() + self.addPlotsToLayout() - def addWidgetsToGridLayout(self, result): + def addWidgetsToGridLayout(self): self.secondParamCombo_subvol_sizes = self.subvol_sizes self.secondParamCombo_subvol_points = self.subvol_points @@ -155,7 +164,7 @@ def addWidgetsToGridLayout(self, result): self.grid_layout.addWidget(self.button,widgetno,2) widgetno+=1 - def addPlotsToLayout(self, result, displ_wrt_point0, mean_array, std_array): + def addPlotsToLayout(self): ''' Extracts the data from the disp file. @@ -167,25 +176,26 @@ def addPlotsToLayout(self, result, displ_wrt_point0, mean_array, std_array): result: RunResults displ_wrt_point0: bool ''' - self.fig.suptitle(f"Run '{result.run_name}': points in subvolume {result.subvol_points}, subvolume size {result.subvol_size}",fontsize='xx-large') - result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) - numGraphs = len(result_arrays) - if numGraphs <= 3: - numRows = 1 - else: - numRows = int(np.round(np.sqrt(numGraphs))) - numColumns = int(np.ceil(numGraphs/numRows)) - + self.fig.clf() + numRows = 2 + numColumns = 2 + if len(self.result_data_frame) > 1: + current_subvol_points = self.subvol_points_widget.currentText() + current_subvol_size = self.subvol_size_widget.currentText() + row = self.selectRow(self.result_data_frame, current_subvol_points, current_subvol_size) + elif len(self.result_data_frame) == 1: + row = self.selectRow(self.result_data_frame, None, None) + result_arrays = row.result_arrays + mean_array = row.mean_array + std_array = row.std_array + self.fig.suptitle(f"Run '{self.run_name}': points in subvolume {row.subvol_points}, subvolume size {row.subvol_size}",fontsize='xx-large') for plotNum, array in enumerate(result_arrays): - data_label = result.data_label[plotNum] + data_label = self.data_label[plotNum] mean = mean_array[plotNum] std = std_array[plotNum] subplot = self.fig.add_subplot(numRows, numColumns, plotNum + 1) self.addHistogramSubplot(subplot, array, data_label, mean, std) - - self.fig.tight_layout(rect=[0, 0, 1, 0.95]) - self.canvas.draw() @@ -247,14 +257,14 @@ def showSecondParam(self): if index == 0: self.secondParamLabel.show() self.secondParamCombo.show() - self.secondParamLabel.setText("Subvolume Size:") + self.secondParamLabel.setText("Subvolume size:") self.secondParamCombo.clear() self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_sizes]) elif index == 1: self.secondParamLabel.show() self.secondParamCombo.show() - self.secondParamLabel.setText("Points in Subvolume:") + self.secondParamLabel.setText("Points in subvolume:") self.secondParamCombo.clear() self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_points]) @@ -263,14 +273,12 @@ def showSecondParam(self): self.secondParamCombo.hide() - class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): print("init2") param_list = ["Subvolume size", "Sampling points in subvolume", "None"] super().__init__(parent, result_data_frame, param_list, "Plot histograms") - def addPlotsToLayout(self): """And stores mean and std"""# print("addplotb") @@ -432,7 +440,6 @@ def __init__(self, parent, object_type, save_only): self.layout.addRow(self.save_button, self.quit_button) self.setLayout(self.layout) - def save(self, save_only): if self.object == "mask": #Load Saved Session diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 9b98e482..76d080de 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -224,12 +224,8 @@ def DeleteAllWidgets(self): def CreateDockWidgets(self, displ_wrt_point0 = False): result_data_frame = createResultsDataFrame(self.results_folder, displ_wrt_point0) result_data_frame = addMeanAndStdToResultDataFrame(result_data_frame) - row = result_data_frame.iloc[0] - result = row.result - mean_array = row.mean_array - std_array = row.std_array - single_run_results_widget = SingleRunResultsWidget(self, result_data_frame, displ_wrt_point0, mean_array, std_array) - dock1 = QDockWidget(result.title,self) + single_run_results_widget = SingleRunResultsWidget(self, result_data_frame) + dock1 = QDockWidget("Single", self) dock1.setFeatures(QDockWidget.NoDockWidgetFeatures) dock1.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) dock1.setWidget(single_run_results_widget) From eb363e215b375ca59fe8cf7cb1f0f9f41bd45275 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 24 Sep 2024 18:31:12 +0100 Subject: [PATCH 18/39] Edit widgets names --- src/idvc/ui/widgets.py | 133 +++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 59 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index db4ebe4c..dfb43180 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -136,8 +136,8 @@ def __init__(self, parent, result_data_frame): self.addPlotsToLayout() def addWidgetsToGridLayout(self): - self.secondParamCombo_subvol_sizes = self.subvol_sizes - self.secondParamCombo_subvol_points = self.subvol_points + self.subvol_size_value_list = self.subvol_sizes + self.subvol_points_value_list = self.subvol_points widgetno=1 @@ -146,7 +146,7 @@ def addWidgetsToGridLayout(self): self.grid_layout.addWidget(self.subvol_points_label,widgetno,1) self.subvol_points_widget = QComboBox(self) - self.subvol_points_widget.addItems(self.secondParamCombo_subvol_points) + self.subvol_points_widget.addItems(self.subvol_points_value_list) self.grid_layout.addWidget(self.subvol_points_widget,widgetno,2) widgetno+=1 @@ -155,7 +155,7 @@ def addWidgetsToGridLayout(self): self.grid_layout.addWidget(self.subvol_size_label,widgetno,1) self.subvol_size_widget = QComboBox(self) - self.subvol_size_widget.addItems(self.secondParamCombo_subvol_sizes) + self.subvol_size_widget.addItems(self.subvol_size_value_list) self.grid_layout.addWidget(self.subvol_size_widget,widgetno,2) widgetno+=1 @@ -209,69 +209,84 @@ def __init__(self, parent, result_data_frame, param_list, button_text = "Plot"): self.addPlotsToLayout() def addWidgetstoGridLayout(self, result, param_list, button_text): - print("second grid") - widgetno=1 + widgetno=0 - self.label = QLabel(self) - self.label.setText("Select result to plot: ") - self.grid_layout.addWidget(self.label,widgetno,1) + self.data_label_label = QLabel(self) + self.data_label_label.setText("Select result to plot: ") + self.grid_layout.addWidget(self.data_label_label,widgetno,1) self.data_label_widget = QComboBox(self) self.data_label_widget.addItems(result.data_label) self.grid_layout.addWidget(self.data_label_widget,widgetno,2) + widgetno+=1 - self.label1 = QLabel(self) - self.label1.setText("Select parameter to fix: ") - self.grid_layout.addWidget(self.label1,widgetno,1) + self.parameter_fix_label = QLabel(self) + self.parameter_fix_label.setText("Select parameter to fix: ") + self.grid_layout.addWidget(self.parameter_fix_label,widgetno,1) - self.param_list_widget = QComboBox(self) - self.param_list_widget.addItems(param_list) + self.parameter_fix_widget = QComboBox(self) + self.parameter_fix_widget.addItems(param_list) + self.grid_layout.addWidget(self.parameter_fix_widget,widgetno,2) - self.grid_layout.addWidget(self.param_list_widget,widgetno,2) widgetno+=1 - self.secondParamLabel = QLabel(self) - self.secondParamLabel.setText("Subvolume size:") - self.grid_layout.addWidget(self.secondParamLabel,widgetno,1) + self.subvol_size_value_label = QLabel(self) + self.subvol_size_value_label.setText("Subvolume size:") + self.grid_layout.addWidget(self.subvol_size_value_label,widgetno,1) - self.secondParamCombo = QComboBox(self) - self.secondParamCombo_subvol_sizes = self.subvol_sizes - self.secondParamCombo_subvol_points = self.subvol_points - self.secondParamCombo.addItems(self.secondParamCombo_subvol_sizes) - self.grid_layout.addWidget(self.secondParamCombo,widgetno,2) - widgetno+=1 - - self.param_list_widget.currentIndexChanged.connect(self.showSecondParam) - self.showSecondParam() + self.subvol_size_value_widget = QComboBox(self) + self.subvol_size_value_list = self.subvol_sizes + self.subvol_size_value_widget.addItems(self.subvol_size_value_list) + self.grid_layout.addWidget(self.subvol_size_value_widget,widgetno,2) + + self.subvol_points_value_label = QLabel(self) + self.subvol_points_value_label.setText("Points in subvolume:") + self.grid_layout.addWidget(self.subvol_points_value_label,widgetno,1) + + self.subvol_points_value_widget = QComboBox(self) + self.subvol_points_value_list = self.subvol_points + self.subvol_points_value_widget.addItems(self.subvol_points_value_list) + self.grid_layout.addWidget(self.subvol_points_value_widget,widgetno,2) + + self.showParameterValues(2) + self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.showParameterValues(2)) + widgetno+=1 self.button = QtWidgets.QPushButton(button_text) self.button.clicked.connect(partial(self.addPlotsToLayout)) - self.grid_layout.addWidget(self.button,widgetno,2) - widgetno+=1 - def showSecondParam(self): - index = self.param_list_widget.currentIndex() + def showParameterValues(self, row): + index = self.parameter_fix_widget.currentIndex() if index == 0: - self.secondParamLabel.show() - self.secondParamCombo.show() - self.secondParamLabel.setText("Subvolume size:") - self.secondParamCombo.clear() - self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_sizes]) + self.subvol_points_value_label.hide() + self.subvol_points_value_widget.hide() + self.grid_layout.removeWidget(self.subvol_points_value_label) + self.grid_layout.removeWidget(self.subvol_points_value_widget) + self.grid_layout.addWidget(self.subvol_size_value_label, row, 1) + self.grid_layout.addWidget(self.subvol_size_value_widget, row, 2) + self.subvol_size_value_label.show() + self.subvol_size_value_widget.show() elif index == 1: - self.secondParamLabel.show() - self.secondParamCombo.show() - self.secondParamLabel.setText("Points in subvolume:") - self.secondParamCombo.clear() - self.secondParamCombo.addItems([str(i) for i in self.secondParamCombo_subvol_points]) + self.grid_layout.addWidget(self.subvol_points_value_label, row,1) + self.grid_layout.addWidget(self.subvol_points_value_widget, row,2) + self.subvol_points_value_label.show() + self.subvol_points_value_widget.show() + self.subvol_size_value_label.hide() + self.subvol_size_value_widget.hide() + self.grid_layout.removeWidget(self.subvol_size_value_label) + self.grid_layout.removeWidget(self.subvol_size_value_widget) elif index ==2: - self.secondParamLabel.hide() - self.secondParamCombo.hide() - + self.subvol_points_value_label.hide() + self.subvol_points_value_widget.hide() + self.subvol_size_value_label.hide() + self.subvol_size_value_widget.hide() + + class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): @@ -283,9 +298,9 @@ def addPlotsToLayout(self): """And stores mean and std"""# print("addplotb") self.fig.clf() - param_index = self.param_list_widget.currentIndex() + param_index = self.parameter_fix_widget.currentIndex() - self.fig.suptitle(f"Bulk Run '{self.run_name}': {self.data_label_widget.currentText()}",fontsize='xx-large') + self.fig.suptitle(f"Bulk run '{self.run_name}': {self.data_label_widget.currentText()} for {self.parameter_fix_widget.currentText()} = ",fontsize='xx-large') numRows = len(self.subvol_sizes) numColumns = len(self.subvol_points) @@ -296,11 +311,11 @@ def addPlotsToLayout(self): if param_index == 0: numRows = 1 - if result.subvol_size != float(self.secondParamCombo.currentText()): + if result.subvol_size != float(self.subvol_size_value_widget.currentText()): continue elif param_index == 1: numColumns = 1 - if result.subvol_points != float(self.secondParamCombo.currentText()): + if result.subvol_points != float(self.subvol_points_value_widget.currentText()): continue data_label = f"{self.data_label_widget.currentText()}" data_index = self.data_label_widget.currentIndex() @@ -320,17 +335,17 @@ def __init__(self, parent, result_data_frame): param_list = ["Subvolume size", "Sampling points in subvolume"] super().__init__(parent, result_data_frame, param_list) - self.secondParamCombo_subvol_sizes = self.subvol_sizes - self.secondParamCombo_subvol_points = self.subvol_points - self.secondParamCombo_subvol_sizes = np.append(self.secondParamCombo_subvol_sizes, "All") - self.secondParamCombo_subvol_points = np.append(self.secondParamCombo_subvol_points, "All") - self.secondParamCombo.addItems(["All"]) + self.subvol_size_value_list = self.subvol_sizes + self.subvol_points_value_list = self.subvol_points + self.subvol_size_value_list = np.append(self.subvol_size_value_list, "All") + self.subvol_points_value_list = np.append(self.subvol_points_value_list, "All") + self.subvol_points_value_widget.addItems(["All"]) + self.subvol_size_value_widget.addItems(["All"]) def addPlotsToLayout(self): - print("addplots") self.fig.clf() df = self.result_data_frame - param_index = self.param_list_widget.currentIndex() + param_index = self.parameter_fix_widget.currentIndex() @@ -345,8 +360,8 @@ def addPlotsToLayout(self): other_type ='subvol_size' numRows = len(self.subvol_sizes) for subvol_size in self.subvol_sizes: - if str(subvol_size) != self.secondParamCombo.currentText(): - if self.secondParamCombo.currentText() == "All": + if str(subvol_size) != self.subvol_size_value_widget.currentText(): + if self.subvol_size_value_widget.currentText() == "All": pass else: continue @@ -373,8 +388,8 @@ def addPlotsToLayout(self): other_type = 'subvol_points' numRows = len(self.subvol_points) for subvol_points in self.subvol_points: - if str(subvol_points) != self.secondParamCombo.currentText(): - if self.secondParamCombo.currentText() == "All": + if str(subvol_points) != self.subvol_points_value_widget.currentText(): + if self.subvol_points_value_widget.currentText() == "All": pass else: continue From b931a506509b1e3a7acc14892ea27851e1737c5e Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 25 Sep 2024 17:57:49 +0100 Subject: [PATCH 19/39] Improve labels in single and bulk tab --- src/idvc/ui/widgets.py | 33 ++++++++++++++++++++++++--------- src/idvc/utilities.py | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index dfb43180..7fcfd77e 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -190,11 +190,13 @@ def addPlotsToLayout(self): std_array = row.std_array self.fig.suptitle(f"Run '{self.run_name}': points in subvolume {row.subvol_points}, subvolume size {row.subvol_size}",fontsize='xx-large') for plotNum, array in enumerate(result_arrays): - data_label = self.data_label[plotNum] + x_label = self.data_label[plotNum] + if 0 Date: Thu, 26 Sep 2024 11:36:24 +0100 Subject: [PATCH 20/39] Add checkbox collapse plots --- src/idvc/ui/widgets.py | 88 ++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 7fcfd77e..ca47b4c9 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -113,12 +113,11 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): plot.legend(loc='upper right') - def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color): + def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label): # Create the plot - subplot.plot(xpoints, ypoints, color+'-') + subplot.plot(xpoints, ypoints, color+'-', label=label) subplot.set_ylabel(ylabel) subplot.set_xlabel(xlabel) - class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code @@ -357,6 +356,30 @@ def __init__(self, parent, result_data_frame): self.subvol_points_value_widget.addItems(["All"]) self.subvol_size_value_widget.addItems(["All"]) + def addWidgetstoGridLayout(self, result, param_list, button_text): + "Moves the button one row down and inserts the checkbox before the button" + super().addWidgetstoGridLayout(result, param_list, button_text) + widgetno = self.grid_layout.rowCount() - 2 + self.collapse_checkbox = QCheckBox("Collapse plots", self) + self.collapse_checkbox.setChecked(False) + self.grid_layout.addWidget(self.collapse_checkbox,widgetno,2) + self.grid_layout.addWidget(self.button,widgetno+1,2) + self.subvol_points_value_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) + self.subvol_size_value_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) + self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) + self.collapse_checkbox.hide() + + def hideShowCollapseCheckbox(self): + param_index = self.parameter_fix_widget.currentIndex() + if param_index == 0: + widget = self.subvol_size_value_widget + elif param_index == 1: + widget = self.subvol_points_value_widget + if widget.currentText() == "All": + self.collapse_checkbox.show() + else: + self.collapse_checkbox.hide() + def addPlotsToLayout(self): self.fig.clf() df = self.result_data_frame @@ -365,7 +388,7 @@ def addPlotsToLayout(self): numColumns = 2 - plotNum = 0 + plotNum = 1 self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') if param_index == 2: pass @@ -373,7 +396,14 @@ def addPlotsToLayout(self): if param_index == 0: data_type = 'subvol_points' other_type ='subvol_size' - numRows = len(self.subvol_sizes) + if not self.collapse_checkbox.isChecked(): + numRows = len(self.subvol_sizes) + else: + numRows = 1 + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot_mean.legend(loc='upper right') + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) + subplot_std.legend(loc='upper right') for subvol_size in self.subvol_sizes: if str(subvol_size) != self.subvol_size_value_widget.currentText(): if self.subvol_size_value_widget.currentText() == "All": @@ -385,23 +415,37 @@ def addPlotsToLayout(self): df_sz = df[(df[other_type] == subvol_size)] xpoints = df_sz[data_type] - plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + + if not self.collapse_checkbox.isChecked(): + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') - subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) + self.addStatisticalAnalysisPlot(subplot_mean, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r', f"{other_type}: {subvol_size}") + subplot_mean.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + if not self.collapse_checkbox.isChecked(): + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label + " std", xpoints,ypoints, 'g') - subplot.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) + self.addStatisticalAnalysisPlot(subplot_std, f"{data_type}", data_label + " std", xpoints,ypoints, 'g', f"{other_type}: {subvol_size}") + subplot_std.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) + + if not self.collapse_checkbox.isChecked(): + plotNum = plotNum + 1 #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) elif param_index == 1: data_type = 'subvol_size' other_type = 'subvol_points' - numRows = len(self.subvol_points) + + if not self.collapse_checkbox.isChecked(): + numRows = len(self.subvol_points) + else: + numRows = 1 + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot_mean.legend(loc='upper right') + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) + subplot_std.legend(loc='upper right') for subvol_points in self.subvol_points: if str(subvol_points) != self.subvol_points_value_widget.currentText(): if self.subvol_points_value_widget.currentText() == "All": @@ -413,17 +457,21 @@ def addPlotsToLayout(self): df_sz = df[(df[other_type] == subvol_points)] xpoints = df_sz[data_type] - plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + if not self.collapse_checkbox.isChecked(): + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r') - subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) + self.addStatisticalAnalysisPlot(subplot_mean, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r',f"{other_type}: {subvol_points}") + subplot_mean.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) plotNum = plotNum + 1 - subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + if not self.collapse_checkbox.isChecked(): + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot, f"{data_type}", data_label + " std", xpoints,ypoints, 'g') - subplot.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) + self.addStatisticalAnalysisPlot(subplot_std, f"{data_type}", data_label + " std", xpoints,ypoints, 'g',f"{other_type}: {subvol_points}") + subplot_std.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) + + if not self.collapse_checkbox.isChecked(): + plotNum = plotNum + 1 self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() From 8d70cb08cd7994f97912be329354354f91c2f6af Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Fri, 27 Sep 2024 15:50:48 +0100 Subject: [PATCH 21/39] Add random colours to collapsed plots and make the code compact --- src/idvc/ui/widgets.py | 154 ++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 87 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index ca47b4c9..ff83d05c 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -79,7 +79,12 @@ def selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_s elif len(df) == 1: df = self.result_data_frame row = df.iloc[0] - return row + return row + + def selectOneParameter(self, result_data_frame, parameter, selected_parameter): + df = result_data_frame + df = df[(df[parameter].astype(str) == selected_parameter)] + return df def addPlotsToLayout(self): pass @@ -114,8 +119,8 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): plot.legend(loc='upper right') def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label): - # Create the plot - subplot.plot(xpoints, ypoints, color+'-', label=label) + print("Create the plot") + subplot.plot(xpoints, ypoints, color=color, linestyle='-', label=label) subplot.set_ylabel(ylabel) subplot.set_xlabel(xlabel) @@ -258,6 +263,8 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.button.clicked.connect(partial(self.addPlotsToLayout)) self.grid_layout.addWidget(self.button,widgetno,2) + self.parameter_value_widget_list = [self.subvol_size_value_widget, self.subvol_points_value_widget] + def showParameterValues(self, row): index = self.parameter_fix_widget.currentIndex() @@ -369,6 +376,22 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.collapse_checkbox.hide() + def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, color_mean = 'r', color_std ='g'): + print("inside meanplot") + df_sz = self.selectOneParameter(result_data_frame, parameter, selected_parameter) + xpoints = df_sz[x_parameter] + ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) + print("xp",xpoints) + print("yp",ypoints) + self.addStatisticalAnalysisPlot(subplot_mean, f"x_parameter {x_parameter}", data_label +" mean",xpoints,ypoints, color_mean, f"{parameter}: {selected_parameter}") + subplot_mean.set_title(f"{parameter}: {selected_parameter}", fontsize='x-large', pad=20) + ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) + self.addStatisticalAnalysisPlot(subplot_std, f"x_parameter {x_parameter}", data_label + " std", xpoints,ypoints, color_std, f"{parameter}: {selected_parameter}") + print("xp",xpoints) + print("yp",ypoints) + subplot_std.set_title(f"{parameter}: {selected_parameter}", fontsize='x-large', pad=20) + + def hideShowCollapseCheckbox(self): param_index = self.parameter_fix_widget.currentIndex() if param_index == 0: @@ -382,96 +405,53 @@ def hideShowCollapseCheckbox(self): def addPlotsToLayout(self): self.fig.clf() + print("inside plot method") df = self.result_data_frame param_index = self.parameter_fix_widget.currentIndex() - - - + print("param_index",param_index) + value_widget = self.parameter_value_widget_list[param_index] + print("value widget",value_widget) numColumns = 2 plotNum = 1 self.fig.suptitle(f"Bulk Run 'self.run_name': self.data_label_widget.currentText()",fontsize='xx-large') - if param_index == 2: - pass - else: - if param_index == 0: - data_type = 'subvol_points' - other_type ='subvol_size' - if not self.collapse_checkbox.isChecked(): - numRows = len(self.subvol_sizes) - else: - numRows = 1 - subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - subplot_mean.legend(loc='upper right') - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) - subplot_std.legend(loc='upper right') - for subvol_size in self.subvol_sizes: - if str(subvol_size) != self.subvol_size_value_widget.currentText(): - if self.subvol_size_value_widget.currentText() == "All": - pass - else: - continue - data_label = f"{self.data_label_widget.currentText()}" - data_index = self.data_label_widget.currentIndex() - df_sz = df[(df[other_type] == subvol_size)] - xpoints = df_sz[data_type] - - - if not self.collapse_checkbox.isChecked(): - subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - - ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_mean, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r', f"{other_type}: {subvol_size}") - subplot_mean.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) - - plotNum = plotNum + 1 - if not self.collapse_checkbox.isChecked(): - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum) - ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_std, f"{data_type}", data_label + " std", xpoints,ypoints, 'g', f"{other_type}: {subvol_size}") - subplot_std.set_title(f"{other_type}: {subvol_size}", fontsize='x-large', pad=20) - - if not self.collapse_checkbox.isChecked(): - plotNum = plotNum + 1 - - #self.fig.subplots_adjust(hspace=0.5,wspace=0.5) - elif param_index == 1: - data_type = 'subvol_size' - other_type = 'subvol_points' - - if not self.collapse_checkbox.isChecked(): - numRows = len(self.subvol_points) - else: - numRows = 1 + data_label = f"{self.data_label_widget.currentText()}" + data_index = self.data_label_widget.currentIndex() + print(value_widget.currentText()) + print("checked",self.collapse_checkbox.isChecked()) + self.value_list = [self.subvol_sizes, self.subvol_points] + label_list = ['subvol_size','subvol_points'] + + if value_widget.currentText() == "All": + print("inside all") + + if not self.collapse_checkbox.isChecked(): + print("not chgecked") + print(self.value_list[param_index]) + numRows = len(self.value_list[param_index]) + for value in self.value_list[param_index]: + print(value) subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - subplot_mean.legend(loc='upper right') - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) - subplot_std.legend(loc='upper right') - for subvol_points in self.subvol_points: - if str(subvol_points) != self.subvol_points_value_widget.currentText(): - if self.subvol_points_value_widget.currentText() == "All": - pass - else: - continue - data_label = f"{self.data_label_widget.currentText()}" - data_index = self.data_label_widget.currentIndex() - df_sz = df[(df[other_type] == subvol_points)] - xpoints = df_sz[data_type] - - if not self.collapse_checkbox.isChecked(): - subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_mean, f"{data_type}", data_label +" mean",xpoints,ypoints, 'r',f"{other_type}: {subvol_points}") - subplot_mean.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) - - plotNum = plotNum + 1 - if not self.collapse_checkbox.isChecked(): - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum) - ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_std, f"{data_type}", data_label + " std", xpoints,ypoints, 'g',f"{other_type}: {subvol_points}") - subplot_std.set_title(f"{other_type}: {subvol_points}", fontsize='x-large', pad=20) - - if not self.collapse_checkbox.isChecked(): - plotNum = plotNum + 1 + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index]) + plotNum = plotNum + 2 + elif self.collapse_checkbox.isChecked(): + numRows = 1 + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) + for value in self.value_list[param_index]: + color1 = np.random.rand(3,) + color2 = np.random.rand(3,) + print("value",value) + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], color1,color2) + subplot_mean.legend(loc='upper right') + subplot_std.legend(loc='upper right') + else: + print("outside all") + numRows = 1 + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) + value = value_widget.currentText() + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index]) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() From 6002d6b7792ad1ffff78fb59c51e857fc7ff9093 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Fri, 27 Sep 2024 17:59:02 +0100 Subject: [PATCH 22/39] Improve labelling code --- src/idvc/ui/widgets.py | 66 +++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index ff83d05c..879f209d 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -121,7 +121,7 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label): print("Create the plot") subplot.plot(xpoints, ypoints, color=color, linestyle='-', label=label) - subplot.set_ylabel(ylabel) + subplot.set_ylabel(ylabel + " (pixels)") subplot.set_xlabel(xlabel) class SingleRunResultsWidget(BaseResultsWidget): @@ -306,15 +306,17 @@ def addPlotsToLayout(self): """And stores mean and std"""# print("addplotb") self.fig.clf() + data_label = self.data_label_widget.currentText() param_index = self.parameter_fix_widget.currentIndex() - if param_index == 0: - parameter_value = self.subvol_size_value_widget.currentText() - plot_title = f"Bulk run '{self.run_name}': {self.data_label_widget.currentText()} for {self.parameter_fix_widget.currentText()} = {parameter_value}" - elif param_index == 1: - parameter_value = self.subvol_points_value_widget.currentText() - plot_title = f"Bulk run '{self.run_name}': {self.data_label_widget.currentText()} for {self.parameter_fix_widget.currentText()} = {parameter_value}" - elif param_index == 2: - plot_title = f"Bulk run '{self.run_name}': {self.data_label_widget.currentText()} for all parameters" + + + if param_index == 2: + plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} distribution for all parameters" + else: + value_widget = self.parameter_value_widget_list[param_index] + parameter_value = value_widget.currentText() + param_text = self.parameter_fix_widget.currentText().lower() + plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} distribution for {param_text} = {parameter_value}" self.fig.suptitle(plot_title,fontsize='xx-large') numRows = len(self.subvol_sizes) @@ -327,17 +329,17 @@ def addPlotsToLayout(self): if param_index == 0: numRows = 1 subplot_title = f"Points in subvolume = {result.subvol_points}" - if result.subvol_size != float(self.subvol_size_value_widget.currentText()): + if result.subvol_size != float(parameter_value): continue elif param_index == 1: numColumns = 1 subplot_title = f"Subvolume size = {result.subvol_size}" - if result.subvol_points != float(self.subvol_points_value_widget.currentText()): + if result.subvol_points != float(parameter_value): continue elif param_index == 2: - subplot_title = f"Points in subvolume = {result.subvol_points}, Subvolume size = {result.subvol_size}" + subplot_title = f"Points in subvolume = {result.subvol_points}, subvolume size = {result.subvol_size}" data_index = self.data_label_widget.currentIndex() - x_label = f"{self.data_label_widget.currentText()}" + x_label = data_label if 0 Date: Mon, 30 Sep 2024 15:53:08 +0100 Subject: [PATCH 23/39] Add option to see std and mean in the same plot for all data --- src/idvc/ui/widgets.py | 135 ++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 879f209d..6d2a9ab4 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -124,6 +124,7 @@ def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, subplot.set_ylabel(ylabel + " (pixels)") subplot.set_xlabel(xlabel) + class SingleRunResultsWidget(BaseResultsWidget): '''creates a dockable widget which will display results from a single run of the DVC code ''' @@ -294,7 +295,6 @@ def showParameterValues(self, row): self.subvol_size_value_label.hide() self.subvol_size_value_widget.hide() - class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): @@ -368,96 +368,133 @@ def __init__(self, parent, result_data_frame): def addWidgetstoGridLayout(self, result, param_list, button_text): "Moves the button one row down and inserts the checkbox before the button" super().addWidgetstoGridLayout(result, param_list, button_text) + self.data_label_widget.addItem("All") widgetno = self.grid_layout.rowCount() - 2 self.collapse_checkbox = QCheckBox("Collapse plots", self) self.collapse_checkbox.setChecked(False) self.grid_layout.addWidget(self.collapse_checkbox,widgetno,2) self.grid_layout.addWidget(self.button,widgetno+1,2) + self.data_label_widget.currentIndexChanged.connect(lambda: self.hideShowAllItemInValueWidget()) self.subvol_points_value_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.subvol_size_value_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.collapse_checkbox.hide() - def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, param_text, parameter, selected_parameter,x_parameter, x_label, color_mean = 'r', color_std ='g'): + def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, x_label, color_mean = 'r', color_std ='g', label_mean = "Mean", label_std = "Std"): print("inside meanplot") df_sz = self.selectOneParameter(result_data_frame, parameter, selected_parameter) xpoints = df_sz[x_parameter] ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_mean, x_label, data_label +" mean",xpoints,ypoints, color_mean, f"{param_text} = {selected_parameter}") + self.addStatisticalAnalysisPlot(subplot_mean, x_label, data_label +" mean",xpoints,ypoints, color_mean, label_mean) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_std, x_label, data_label + " std", xpoints,ypoints, color_std, f"{param_text} = {selected_parameter}") - + self.addStatisticalAnalysisPlot(subplot_std, x_label, data_label + " std", xpoints,ypoints, color_std, label_std) + + def hideShowAllItemInValueWidget(self): + index_ss = self.subvol_size_value_widget.findText("All") + index_sp = self.subvol_points_value_widget.findText("All") + if self.data_label_widget.currentText() == "All": + self.subvol_size_value_widget.removeItem(index_ss) + self.subvol_points_value_widget.removeItem(index_sp)) + else: + if index_ss == -1: + self.subvol_size_value_widget.addItem("All") + if index_sp == -1: + self.subvol_points_value_widget.addItem("All") def hideShowCollapseCheckbox(self): param_index = self.parameter_fix_widget.currentIndex() if param_index == 0: widget = self.subvol_size_value_widget elif param_index == 1: - widget = self.subvol_points_value_widget + widget = self.subvol_points_value_widget if widget.currentText() == "All": self.collapse_checkbox.show() else: self.collapse_checkbox.hide() + def addPlotsToLayout(self): self.fig.clf() df = self.result_data_frame + + + + param_index = self.parameter_fix_widget.currentIndex() param_text = self.parameter_fix_widget.currentText().lower() x_label = self.parameter_fix_widget.itemText(1 - param_index) value_widget = self.parameter_value_widget_list[param_index] - numColumns = 2 - plotNum = 1 + + - data_label = f"{self.data_label_widget.currentText()}" - data_index = self.data_label_widget.currentIndex() - print(value_widget.currentText()) - print("checked",self.collapse_checkbox.isChecked()) self.value_list = [self.subvol_sizes, self.subvol_points] label_list = ['subvol_size','subvol_points'] - - if value_widget.currentText() == "All": - print("inside all") - plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for fixed {param_text}" - if not self.collapse_checkbox.isChecked(): - print("not chgecked") - print(self.value_list[param_index]) - numRows = len(self.value_list[param_index]) - for value in self.value_list[param_index]: - print(value) + + numColumns = 2 + plotNum = 1 + if self.data_label_widget.currentText() == "All": + for data_index in range(4): + data_label = f"{self.data_label_widget.itemText(data_index)}" + numRows = 2 + numColumns = 2 + subplot = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot.set_title(f"{data_label}", fontsize='x-large', pad=20) + value = value_widget.currentText() + plot_title = f"Bulk run '{self.run_name}': mean and standard deviation for {param_text} = {value}" + twin = subplot.twinx() + self.meanStdPlots(subplot, twin, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label) + lines1, labels1 = subplot.get_legend_handles_labels() + lines2, labels2 = twin.get_legend_handles_labels() + lines = lines1 + lines2 + labels = labels1 + labels2 + subplot.legend(lines, labels, loc='upper right') + plotNum += 1 + else: + data_index = self.data_label_widget.currentIndex() + data_label = f"{self.data_label_widget.currentText()}" + + if value_widget.currentText() == "All": + print("inside all") + plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for fixed {param_text}" + if not self.collapse_checkbox.isChecked(): + print("not chgecked") + print(self.value_list[param_index]) + numRows = len(self.value_list[param_index]) + for value in self.value_list[param_index]: + print(value) + subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) + subplot_mean.set_title(f"Mean for {param_text} = {value}", fontsize='x-large', pad=20) + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) + subplot_std.set_title(f"Standard deviation for {param_text} = {value}", fontsize='x-large', pad=20) + label = f"{param_text} = {value}" + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label, label_mean = label, label_std = label) + plotNum = plotNum + 2 + elif self.collapse_checkbox.isChecked(): + numRows = 1 subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - subplot_mean.set_title(f"Mean for {param_text} = {value}", fontsize='x-large', pad=20) + subplot_mean.set_title("Mean", fontsize='x-large', pad=20) subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) - subplot_std.set_title(f"Standard deviation for {param_text} = {value}", fontsize='x-large', pad=20) - - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, param_text, label_list[param_index], value, label_list[1-param_index], x_label) - plotNum = plotNum + 2 - elif self.collapse_checkbox.isChecked(): + subplot_std.set_title("Standard deviation", fontsize='x-large', pad=20) + + for value in self.value_list[param_index]: + color1 = np.random.rand(3,) + color2 = np.random.rand(3,) + label = f"{param_text} = {value}" + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label, color1,color2, label, label) + subplot_mean.legend(loc='upper right') + subplot_std.legend(loc='upper right') + else: + + print("outside all") numRows = 1 subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) subplot_mean.set_title("Mean", fontsize='x-large', pad=20) - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) + subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) subplot_std.set_title("Standard deviation", fontsize='x-large', pad=20) - - for value in self.value_list[param_index]: - color1 = np.random.rand(3,) - color2 = np.random.rand(3,) - print("value",value) - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, param_text, label_list[param_index], value, label_list[1-param_index], x_label, color1,color2) - subplot_mean.legend(loc='upper right') - subplot_std.legend(loc='upper right') - else: - - print("outside all") - numRows = 1 - subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) - subplot_mean.set_title("Mean", fontsize='x-large', pad=20) - subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum+1) - subplot_std.set_title("Standard deviation", fontsize='x-large', pad=20) - value = value_widget.currentText() - plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for {param_text} = {value}" - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, param_text, label_list[param_index], value, label_list[1-param_index], x_label) - + value = value_widget.currentText() + plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for {param_text} = {value}" + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label) + self.fig.suptitle(plot_title,fontsize='xx-large') self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() From ab088f040a5accfb802fe152043b817123655d2a Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 1 Oct 2024 11:49:22 +0100 Subject: [PATCH 24/39] Improve color scheme and lines --- src/idvc/ui/widgets.py | 65 +++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 6d2a9ab4..4a43e754 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -3,8 +3,9 @@ from PySide2.QtCore import * from PySide2.QtGui import * import numpy as np -import matplotlib.pyplot as plt from matplotlib.figure import Figure +from matplotlib import rcParams +from cycler import cycler from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar from idvc.utils.manipulate_result_files import extractDataFromDispResultFile @@ -46,6 +47,19 @@ def __init__(self, parent, result_data_frame): self.layout.addLayout(self.grid_layout,0) self.addInfotoGridLayout(single_result) + self.color_list = [ + '#1f77b4', # Blue + '#ff7f0e', # Orange + '#2ca02c', # Green + '#d62728', # Red + '#9467bd', # Purple + '#8c564b', # Brown + '#e377c2', # Pink + '#7f7f7f', # Gray + '#bcbd22', # Olive + '#17becf' # Cyan + ] + self.linewidth = 3 #pixels? self.fig = Figure(figsize=(10, 8)) self.canvas = FigureCanvas(self.fig) self.fig.clf() @@ -90,7 +104,6 @@ def addPlotsToLayout(self): pass def addWidgetsToGridLayout(self, result): - print("parent grid") pass def addHistogramSubplot(self, plot, array, xlabel, mean, std): @@ -105,22 +118,21 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): relative_counts = counts*100/ len(array) plot.cla() bin_widths = np.diff(bins) - plot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge') + plot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge',color='lightgrey') plot.set_ylabel("Relative frequency (% points in run)") plot.set_xlabel(xlabel) - plot.axvline(mean, color='r', linestyle='--', label=f'mean = {mean:.3f}') - plot.axvline(mean-std, color='g', linestyle='--', label=f'std = {std:.3f}') - plot.axvline(mean+std, color='g', linestyle='--') + plot.axvline(mean, color=self.color_list[0], linestyle='--', linewidth=self.linewidth, label=f'mean = {mean:.3f}') + plot.axvline(mean-std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth, label=f'std = {std:.3f}') + plot.axvline(mean+std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth) x = np.linspace(min(array), max(array), 1000) gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 - plot.plot(x, gaussian, 'b--', label='gaussian fit') + plot.plot(x, gaussian, self.color_list[2],linestyle='--', linewidth=self.linewidth, label='gaussian fit') plot.legend(loc='upper right') - def addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label): - print("Create the plot") - subplot.plot(xpoints, ypoints, color=color, linestyle='-', label=label) + def _addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label, linestyle): + subplot.plot(xpoints, ypoints, color=color, linestyle=linestyle, linewidth=self.linewidth, label=label) subplot.set_ylabel(ylabel + " (pixels)") subplot.set_xlabel(xlabel) @@ -298,13 +310,11 @@ def showParameterValues(self, row): class BulkRunResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): - print("init2") param_list = ["Subvolume size", "Sampling points in subvolume", "None"] super().__init__(parent, result_data_frame, param_list, "Plot histograms") def addPlotsToLayout(self): - """And stores mean and std"""# - print("addplotb") + """And stores mean and std""" self.fig.clf() data_label = self.data_label_widget.currentText() param_index = self.parameter_fix_widget.currentIndex() @@ -354,8 +364,8 @@ def addPlotsToLayout(self): class StatisticsResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): - print("init 3") param_list = ["Subvolume size", "Sampling points in subvolume"] + self.linestyles = ['-','--','-.', ':'] super().__init__(parent, result_data_frame, param_list) self.subvol_size_value_list = self.subvol_sizes @@ -380,21 +390,20 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.collapse_checkbox.hide() - def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, x_label, color_mean = 'r', color_std ='g', label_mean = "Mean", label_std = "Std"): - print("inside meanplot") + def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, x_label, color_mean = '#1f77b4', color_std = '#ff7f0e', label_mean = "Mean", label_std = "Std", linestyle = '-'): df_sz = self.selectOneParameter(result_data_frame, parameter, selected_parameter) xpoints = df_sz[x_parameter] ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_mean, x_label, data_label +" mean",xpoints,ypoints, color_mean, label_mean) + self._addStatisticalAnalysisPlot(subplot_mean, x_label, data_label +" mean",xpoints,ypoints, color_mean, label_mean, linestyle) ypoints = df_sz['std_array'].apply(lambda array: array[data_index]) - self.addStatisticalAnalysisPlot(subplot_std, x_label, data_label + " std", xpoints,ypoints, color_std, label_std) + self._addStatisticalAnalysisPlot(subplot_std, x_label, data_label + " std", xpoints,ypoints, color_std, label_std, linestyle) def hideShowAllItemInValueWidget(self): index_ss = self.subvol_size_value_widget.findText("All") index_sp = self.subvol_points_value_widget.findText("All") if self.data_label_widget.currentText() == "All": self.subvol_size_value_widget.removeItem(index_ss) - self.subvol_points_value_widget.removeItem(index_sp)) + self.subvol_points_value_widget.removeItem(index_sp) else: if index_ss == -1: self.subvol_size_value_widget.addItem("All") @@ -454,14 +463,10 @@ def addPlotsToLayout(self): data_label = f"{self.data_label_widget.currentText()}" if value_widget.currentText() == "All": - print("inside all") plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for fixed {param_text}" if not self.collapse_checkbox.isChecked(): - print("not chgecked") - print(self.value_list[param_index]) numRows = len(self.value_list[param_index]) for value in self.value_list[param_index]: - print(value) subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) subplot_mean.set_title(f"Mean for {param_text} = {value}", fontsize='x-large', pad=20) subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) @@ -476,16 +481,17 @@ def addPlotsToLayout(self): subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) subplot_std.set_title("Standard deviation", fontsize='x-large', pad=20) - for value in self.value_list[param_index]: - color1 = np.random.rand(3,) - color2 = np.random.rand(3,) + for i, value in enumerate(self.value_list[param_index]): + linestyle = self.linestyles[i // len(self.color_list) % len(self.linestyles)] + if len(self.result_data_frame) <= len(self.color_list) * len(self.linestyles): + color = self.color_list[i % len(self.color_list)] + else: + color = np.random.rand(3,) label = f"{param_text} = {value}" - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label, color1,color2, label, label) + self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label, label_list[param_index], value, label_list[1-param_index], x_label, color, color, label, label, linestyle = linestyle) subplot_mean.legend(loc='upper right') subplot_std.legend(loc='upper right') else: - - print("outside all") numRows = 1 subplot_mean = self.fig.add_subplot(numRows, numColumns, plotNum) subplot_mean.set_title("Mean", fontsize='x-large', pad=20) @@ -508,7 +514,6 @@ class SaveObjectWindow(QtWidgets.QWidget): def __init__(self, parent, object_type, save_only): super().__init__(parent = parent) - #print(save_only) self.object = object_type if self.object == "mask": From 0ac013fdc1ea280a3f9daac0518aab5040e249b6 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 1 Oct 2024 12:03:23 +0100 Subject: [PATCH 25/39] Use numpy reader instead of point cloud converter to read data --- src/idvc/utils/manipulate_result_files.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py index a4f6773b..35fe719a 100644 --- a/src/idvc/utils/manipulate_result_files.py +++ b/src/idvc/utils/manipulate_result_files.py @@ -7,13 +7,11 @@ def extractDataFromDispResultFile(result, displ_wrt_point0): """ Gets the filepath of the disp file via `result.disp_file`. - Extracts the data in the numpy format by using the converter, does not load the label row. + Extracts the data in the numpy format, skips the header. This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. """ - data = np.asarray( - PointCloudConverter.loadPointCloudFromCSV(result.disp_file,'\t')[:] - ) + data = np.genfromtxt(result.disp_file, delimiter='\t', skip_header=1) data_shape = data.shape index_objmin = 5 index_disp = [6,9] From d4be2206ffd17fe937950b240c06c1e031bb7b0f Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 1 Oct 2024 13:05:20 +0100 Subject: [PATCH 26/39] Add option to edit fontsize in settings --- src/idvc/idvc.py | 7 +++++-- src/idvc/ui/dialogs.py | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index 4c816761..994a4bde 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -1,5 +1,5 @@ import PySide2 -from PySide2 import QtWidgets, QtGui +from PySide2 import QtWidgets, QtGui, QtCore import os, sys import logging import argparse @@ -16,8 +16,11 @@ def main(): level = eval(f'logging.{args.debug.upper()}') logging.basicConfig(level=level) logging.info(f"iDVC: Setting debugging level to {args.debug.upper()}") - app = QtWidgets.QApplication([]) + # Set a global font for the application + font = QtGui.QFont("Arial", 12) # Replace with your preferred font and size + QtWidgets.QApplication.setFont(font) + file_dir = os.path.dirname(__file__) owl_file = os.path.join(file_dir, "DVCIconSquare.png") diff --git a/src/idvc/ui/dialogs.py b/src/idvc/ui/dialogs.py index 384a4aad..4f590d0e 100644 --- a/src/idvc/ui/dialogs.py +++ b/src/idvc/ui/dialogs.py @@ -17,7 +17,13 @@ def __init__(self, parent, title="Settings"): self.parent = parent - + self.fontsize_label = QLabel("Fontsize: ") + self.fontsize_widget = QSpinBox() + self.fontsize_widget.setMaximum(25) + self.fontsize_widget.setMinimum(5) + self.fontsize_widget.setSingleStep(1) + self.fontsize_widget.setValue(12) + self.addWidget(self.fontsize_widget, self.fontsize_label, 'fontsize') self.dark_checkbox = QCheckBox("Dark Mode") # populate from settings if self.parent.settings.value("dark_mode") is not None: @@ -105,6 +111,8 @@ def __init__(self, parent, title="Settings"): def onOk(self): + font = PySide2.QtGui.QFont("Arial", self.fontsize_widget.value()) + PySide2.QtWidgets.QApplication.setFont(font) #self.parent.settings.setValue("settings_chosen", 1) if self.dark_checkbox.isChecked(): self.parent.settings.setValue("dark_mode", True) From a14e9bc16ccdb57ad5a24191a37aa8e2591556a2 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 1 Oct 2024 17:12:57 +0100 Subject: [PATCH 27/39] Fix sizing --- src/idvc/ui/widgets.py | 68 +++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 4a43e754..1cd8f112 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -3,12 +3,10 @@ from PySide2.QtCore import * from PySide2.QtGui import * import numpy as np +import matplotlib from matplotlib.figure import Figure -from matplotlib import rcParams -from cycler import cycler from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar -from idvc.utils.manipulate_result_files import extractDataFromDispResultFile from functools import partial import shutil import os @@ -60,11 +58,20 @@ def __init__(self, parent, result_data_frame): '#17becf' # Cyan ] self.linewidth = 3 #pixels? - self.fig = Figure(figsize=(10, 8)) + self.fontsizes = {'figure_title':18, 'subplot_title':14, 'label':10} + self.figsize = (8, 4) # Size in inches + self.dpi = 100 + self.fig = Figure(figsize=self.figsize, dpi=self.dpi) self.canvas = FigureCanvas(self.fig) - self.fig.clf() - self.canvas.setMinimumSize(400, 400) #needed for scrollbar + #self.fig.clf() + self.canvas.setMinimumSize(800, 400) #needed for scrollbar self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + #self.canvas.setFixedSize(1200, 600) + # Set the canvas size policy to allow width expansion only + + # Set a fixed height for the canvas (you can adjust the height as needed) + #self.canvas.setFixedHeight(800) # Example fixed height + self.toolbar = NavigationToolbar(self.canvas, self) self.layout.addWidget(self.toolbar) @@ -119,8 +126,8 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): plot.cla() bin_widths = np.diff(bins) plot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge',color='lightgrey') - plot.set_ylabel("Relative frequency (% points in run)") - plot.set_xlabel(xlabel) + plot.set_ylabel("Relative frequency (% points in run)", fontsize=self.fontsizes['label']) + plot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) plot.axvline(mean, color=self.color_list[0], linestyle='--', linewidth=self.linewidth, label=f'mean = {mean:.3f}') plot.axvline(mean-std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth, label=f'std = {std:.3f}') plot.axvline(mean+std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth) @@ -133,8 +140,8 @@ def addHistogramSubplot(self, plot, array, xlabel, mean, std): def _addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label, linestyle): subplot.plot(xpoints, ypoints, color=color, linestyle=linestyle, linewidth=self.linewidth, label=label) - subplot.set_ylabel(ylabel + " (pixels)") - subplot.set_xlabel(xlabel) + subplot.set_ylabel(ylabel + " (pixels)", fontsize=self.fontsizes['label']) + subplot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) class SingleRunResultsWidget(BaseResultsWidget): @@ -205,7 +212,7 @@ def addPlotsToLayout(self): result_arrays = row.result_arrays mean_array = row.mean_array std_array = row.std_array - self.fig.suptitle(f"Run '{self.run_name}': points in subvolume {row.subvol_points}, subvolume size {row.subvol_size}",fontsize='xx-large') + self.fig.suptitle(f"Run '{self.run_name}': points in subvolume {row.subvol_points}, subvolume size {row.subvol_size}",fontsize=self.fontsizes['figure_title']) for plotNum, array in enumerate(result_arrays): x_label = self.data_label[plotNum] if 0 Date: Wed, 2 Oct 2024 11:21:24 +0100 Subject: [PATCH 28/39] Add docstrings to base class --- src/idvc/ui/widgets.py | 147 ++++++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 1cd8f112..cb49e57e 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -17,18 +17,27 @@ class BaseResultsWidget(QtWidgets.QWidget): - '''creates a dockable widget which will display graph results from runs of the DVC code + ''' + Creates a widget which can be set in a QDockWidget. ''' def __init__(self, parent, result_data_frame): ''' - Adds vertical layout. - figure. - canvas. - sets minimum size so scrollbar works. + Creates the attributes, including a list of fontsizes and linewidth for the plots, and + a list of colour-blind friendly colours taken from matplotlib + (https://matplotlib.org/stable/users/explain/colors/colors.html#colors-def). + Initialises the Qwidget, adds a vertical layout to it. + Adds a grid layout containing information about the results to the vertical layout. + Creates a figure to be added in a canvas. The canvas is added in a scrool bar wherby + its size must be set to a minimum. The figure size and dpi are fixed + for consistency among screens. The canvas size policy must be set to expandable + to occupy all of the available space in both directions. + A toolbar and the scroll bar are added to the vertical layout. + Parameters ---------- - results: RunResults - displ_wrt_point0: bool + parent : QWidget + result_data_frame : pandas dataframe + Columns: 'subvol_size', 'subvol_points', 'result', 'result_arrays', 'mean_array', 'std_array'. ''' self.result_data_frame = result_data_frame single_result = result_data_frame.iloc[0]['result'] @@ -36,15 +45,6 @@ def __init__(self, parent, result_data_frame): self.data_label = single_result.data_label self.subvol_sizes = result_data_frame['subvol_size'].unique() self.subvol_points = result_data_frame['subvol_points'].unique() - super().__init__(parent = parent) - self.layout = QtWidgets.QVBoxLayout() - self.setLayout(self.layout) - - self.grid_layout = QtWidgets.QGridLayout() - self.grid_layout.setAlignment(Qt.AlignTop) - self.layout.addLayout(self.grid_layout,0) - self.addInfotoGridLayout(single_result) - self.color_list = [ '#1f77b4', # Blue '#ff7f0e', # Orange @@ -59,28 +59,26 @@ def __init__(self, parent, result_data_frame): ] self.linewidth = 3 #pixels? self.fontsizes = {'figure_title':18, 'subplot_title':14, 'label':10} + super().__init__(parent = parent) + self.layout = QtWidgets.QVBoxLayout() + self.setLayout(self.layout) + self.grid_layout = QtWidgets.QGridLayout() + self.grid_layout.setAlignment(Qt.AlignTop) + self.layout.addLayout(self.grid_layout,0) + self.addInfotoGridLayout(single_result) self.figsize = (8, 4) # Size in inches self.dpi = 100 self.fig = Figure(figsize=self.figsize, dpi=self.dpi) self.canvas = FigureCanvas(self.fig) - #self.fig.clf() self.canvas.setMinimumSize(800, 400) #needed for scrollbar self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - #self.canvas.setFixedSize(1200, 600) - # Set the canvas size policy to allow width expansion only - - # Set a fixed height for the canvas (you can adjust the height as needed) - #self.canvas.setFixedHeight(800) # Example fixed height - - self.toolbar = NavigationToolbar(self.canvas, self) self.layout.addWidget(self.toolbar) - scroll_area_widget = NoBorderScrollArea(self.canvas) self.layout.addWidget(scroll_area_widget,1) def addInfotoGridLayout(self, result): - widgetno=0 + '''Adds a QLabel widget containing information about the results to the grid layout.''' self.results_details_label = QLabel(self) self.results_details_label.setText("Subvolume Geometry: {subvol_geom}\n\ Maximum Displacement: {disp_max}\n\ @@ -90,10 +88,30 @@ def addInfotoGridLayout(self, result): Rigid Body Offset: {rigid_trans}".format(subvol_geom=result.subvol_geom, \ disp_max=result.disp_max, num_srch_dof=str(result.num_srch_dof), obj_function=result.obj_function, \ interp_type=result.interp_type, rigid_trans=str(result.rigid_trans))) - self.grid_layout.addWidget(self.results_details_label,widgetno,0,5,1) + self.grid_layout.addWidget(self.results_details_label,0,0,5,1) self.results_details_label.setAlignment(Qt.AlignTop) - def selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_size): + def addPlotsToLayout(self): + '''To be defined in the child classes.''' + pass + + def addWidgetsToGridLayout(self, result): + '''To be defined in the child classes.''' + pass + + def _selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_size): + '''Given a dataframe, returns the whole row whose 'subvol_points' and 'subvol_size' columns + are associated with the selected values. + + Parameters + ---------- + result_data_frame : pandas dataframe + Includes the 'subvol_points' and 'subvol_size' columns. + selected_subvol_points : str of an integer + Selected points in subvolume. + selected_subvol_size : str of an integer + Selected subvolume size. + ''' df = result_data_frame if len(df) > 1: df = df[(df['subvol_points'].astype(str) == selected_subvol_points) & (df['subvol_size'].astype(str) == selected_subvol_size)] @@ -102,43 +120,54 @@ def selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_s row = df.iloc[0] return row - def selectOneParameter(self, result_data_frame, parameter, selected_parameter): + def _selectOneParameter(self, result_data_frame, parameter, selected_parameter): + '''Given a dataframe, returns a filtered dataframe whose rows are associated with + the selected value of a parameter. + + Parameters + ---------- + result_data_frame : pandas dataframe + Includes the 'subvol_points' and 'subvol_size' columns. + parameter : str + 'subvol_points' or 'subvol_size' + selected_parameter : str of an integer + Selected value of the parameter. + ''' df = result_data_frame df = df[(df[parameter].astype(str) == selected_parameter)] return df - def addPlotsToLayout(self): - pass - - def addWidgetsToGridLayout(self, result): - pass + def _addHistogramSubplot(self, subplot, array, xlabel, mean, std): + '''Given an array, calculates the relative counts by using the mtplotlib + histogram functionality. It clears the plot and plots the relative frequency histogram + as a bar plot. Sets the x an y labels. Adds the mean and std values as vertical lines. + Plots the gaussian fit. Adds the legend to the plot. - def addHistogramSubplot(self, plot, array, xlabel, mean, std): - '''plot the Gaussian curve, legend - - Returns - ------- - matplotlib.pyplot - A plot of the histogram - """''' - counts, bins = plot.hist(array, bins=20)[0:2] + Parameters + ---------- + plot : matplotlib subplot of a figure + array : numpyarray + xlabel : str + mean : float + std : float + ''' + counts, bins = subplot.hist(array, bins=20)[0:2] relative_counts = counts*100/ len(array) - plot.cla() + subplot.cla() bin_widths = np.diff(bins) - plot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge',color='lightgrey') - plot.set_ylabel("Relative frequency (% points in run)", fontsize=self.fontsizes['label']) - plot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) - plot.axvline(mean, color=self.color_list[0], linestyle='--', linewidth=self.linewidth, label=f'mean = {mean:.3f}') - plot.axvline(mean-std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth, label=f'std = {std:.3f}') - plot.axvline(mean+std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth) - + subplot.bar(bins[:-1], relative_counts, width=bin_widths, align='edge',color='lightgrey') + subplot.set_ylabel("Relative frequency (% points in run)", fontsize=self.fontsizes['label']) + subplot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) + subplot.axvline(mean, color=self.color_list[0], linestyle='--', linewidth=self.linewidth, label=f'mean = {mean:.3f}') + subplot.axvline(mean-std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth, label=f'std = {std:.3f}') + subplot.axvline(mean+std, color=self.color_list[1], linestyle='--', linewidth=self.linewidth) x = np.linspace(min(array), max(array), 1000) gaussian = norm.pdf(x, mean, std) * (bins[1] - bins[0]) *100 - plot.plot(x, gaussian, self.color_list[2],linestyle='--', linewidth=self.linewidth, label='gaussian fit') - - plot.legend(loc='upper right') + subplot.plot(x, gaussian, self.color_list[2],linestyle='--', linewidth=self.linewidth, label='gaussian fit') + subplot.legend(loc='upper right') def _addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label, linestyle): + "Draws a line plot in subplot. Adds labels and sets user defined properties." subplot.plot(xpoints, ypoints, color=color, linestyle=linestyle, linewidth=self.linewidth, label=label) subplot.set_ylabel(ylabel + " (pixels)", fontsize=self.fontsizes['label']) subplot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) @@ -206,9 +235,9 @@ def addPlotsToLayout(self): if len(self.result_data_frame) > 1: current_subvol_points = self.subvol_points_widget.currentText() current_subvol_size = self.subvol_size_widget.currentText() - row = self.selectRow(self.result_data_frame, current_subvol_points, current_subvol_size) + row = self._selectRow(self.result_data_frame, current_subvol_points, current_subvol_size) elif len(self.result_data_frame) == 1: - row = self.selectRow(self.result_data_frame, None, None) + row = self._selectRow(self.result_data_frame, None, None) result_arrays = row.result_arrays mean_array = row.mean_array std_array = row.std_array @@ -220,7 +249,7 @@ def addPlotsToLayout(self): mean = mean_array[plotNum] std = std_array[plotNum] subplot = self.fig.add_subplot(numRows, numColumns, plotNum + 1) - self.addHistogramSubplot(subplot, array, x_label, mean, std) + self._addHistogramSubplot(subplot, array, x_label, mean, std) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() @@ -368,7 +397,7 @@ def addPlotsToLayout(self): std = row.std_array[data_index] plotNum = plotNum + 1 subplot = self.fig.add_subplot(numRows, numColumns, plotNum) - self.addHistogramSubplot(subplot, row.result_arrays[data_index], x_label, mean, std) + self._addHistogramSubplot(subplot, row.result_arrays[data_index], x_label, mean, std) subplot.set_title(subplot_title, pad=20, fontsize=self.fontsizes['subplot_title']) self.fig.subplots_adjust(hspace=2,wspace=0.5) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) @@ -403,7 +432,7 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.collapse_checkbox.hide() def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, x_label, color_mean = '#1f77b4', color_std = '#ff7f0e', label_mean = "Mean", label_std = "Std", linestyle = '-'): - df_sz = self.selectOneParameter(result_data_frame, parameter, selected_parameter) + df_sz = self._selectOneParameter(result_data_frame, parameter, selected_parameter) xpoints = df_sz[x_parameter] ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) self._addStatisticalAnalysisPlot(subplot_mean, x_label, data_label+"mean",xpoints,ypoints, color_mean, label_mean, linestyle) From 586796b9424b2d412c65a2902f098b3693274ed6 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 2 Oct 2024 16:09:19 +0100 Subject: [PATCH 29/39] Add docstrings and remove non necessary attributes --- src/idvc/ui/widgets.py | 215 ++++++++++++++++++++++++++++++----------- 1 file changed, 161 insertions(+), 54 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index cb49e57e..7973270c 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -91,11 +91,11 @@ def addInfotoGridLayout(self, result): self.grid_layout.addWidget(self.results_details_label,0,0,5,1) self.results_details_label.setAlignment(Qt.AlignTop) - def addPlotsToLayout(self): + def addSubplotsToFigure(self): '''To be defined in the child classes.''' pass - def addWidgetsToGridLayout(self, result): + def addWidgetsToGridLayout(self): '''To be defined in the child classes.''' pass @@ -174,24 +174,33 @@ def _addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, class SingleRunResultsWidget(BaseResultsWidget): - '''creates a dockable widget which will display results from a single run of the DVC code + ''' + Creates a widget which can be set in a QDockWidget. + This will display results from a single run of the DVC code. ''' def __init__(self, parent, result_data_frame): ''' + Initialises the SingleRunResultsWidget. + Parameters - ---------- - results: RunResults - displ_wrt_point0: bool + ---------- + parent : QWidget + The parent widget. + result_data_frame : DataFrame + A pandas DataFrame containing the result data. ''' super().__init__(parent, result_data_frame) if len(result_data_frame) > 1: self.addWidgetsToGridLayout() - self.addPlotsToLayout() + self.addSubplotsToFigure() def addWidgetsToGridLayout(self): - self.subvol_size_value_list = self.subvol_sizes - self.subvol_points_value_list = self.subvol_points - + """ + Initialises and adds the following widgets to the grid layout: + - a QLabel and QComboBox for selecting points in a subvolume. + - a QLabel and QComboBox for selecting the size of a subvolume. + - a QPushButton for plotting histograms, which is connected to the `addSubplotsToFigure` method. + """ widgetno=1 self.subvol_points_label = QLabel(self) @@ -199,7 +208,7 @@ def addWidgetsToGridLayout(self): self.grid_layout.addWidget(self.subvol_points_label,widgetno,1) self.subvol_points_widget = QComboBox(self) - self.subvol_points_widget.addItems(self.subvol_points_value_list) + self.subvol_points_widget.addItems(self.subvol_points) self.grid_layout.addWidget(self.subvol_points_widget,widgetno,2) widgetno+=1 @@ -208,26 +217,43 @@ def addWidgetsToGridLayout(self): self.grid_layout.addWidget(self.subvol_size_label,widgetno,1) self.subvol_size_widget = QComboBox(self) - self.subvol_size_widget.addItems(self.subvol_size_value_list) + self.subvol_size_widget.addItems(self.subvol_sizes) self.grid_layout.addWidget(self.subvol_size_widget,widgetno,2) widgetno+=1 self.button = QtWidgets.QPushButton("Plot histograms") - self.button.clicked.connect(partial(self.addPlotsToLayout)) + self.button.clicked.connect(partial(self.addSubplotsToFigure)) self.grid_layout.addWidget(self.button,widgetno,2) widgetno+=1 - def addPlotsToLayout(self): + def addSubplotsToFigure(self): ''' - - Extracts the data from the disp file. - Determines the number of graphs, rows and column to group them in. - Hist makes an instogram with bins 20. + Clears the current figure. Determines the number of rows and + columns for the figure layout. Selects the appropriate row + from the result data frame based on the user selected subvolume points and size. + Extracts result arrays, mean array, and standard deviation array from the selected row. + Sets the figure title with details about the run and subvolume. + Iterates over the result arrays to create histograms and adds them as subplots. + Adjusts the figure layout and redraws the canvas. - Parameters - ---------- - result: RunResults - displ_wrt_point0: bool + Attributes + ---------- + result_data_frame : DataFrame + Data frame containing the results. + subvol_points_widget : QWidget + Widget for selecting subvolume points. + subvol_size_widget : QWidget + Widget for selecting subvolume size. + fig : Figure + Matplotlib figure object. + run_name : str + Name of the current run. + data_label : list + List of labels for the result data. + fontsizes : dict + Dictionary containing font sizes for various elements. + canvas : FigureCanvas + Matplotlib canvas object for rendering the figure. ''' self.fig.clf() numRows = 2 @@ -255,16 +281,46 @@ def addPlotsToLayout(self): class BulkRunResultsBaseWidget(BaseResultsWidget): - '''creates a dockable widget which will display results from all runs in a bulk run + ''' + Creates a baseclass widget with common functionality for displaying results from several results in a bulk run. ''' def __init__(self, parent, result_data_frame, param_list, button_text = "Plot"): + ''' + Initialises the BulkRunResultsBaseWidget. + + Parameters + ---------- + parent : QWidget + The parent widget. + result_data_frame : DataFrame + A pandas DataFrame containing the result data. + param_list : list of str + The text items for the QComboBox. + button_text : str, optional + The text to display on the QPushButton. + ''' super().__init__(parent, result_data_frame) single_result = result_data_frame.iloc[0]['result'] - self.addWidgetstoGridLayout(single_result, param_list, button_text) - self.addPlotsToLayout() + self.addWidgetstoGridLayout(param_list, button_text) + self.addSubplotsToFigure() + def addWidgetstoGridLayout(self, param_list, button_text): + """ + Adds the following widgets to the central and right column of the grid layout: + - a QLabel and a QComboBox for selecting the result to plot. + - a QLabel and a QComboBox for selecting the parameter to fix. + - a QLabel and QComboBox for selecting points in subvolume. + - a QLabel and QComboBox for selecting the size of the subvolume. + - a QPushButton for plotting histograms, which is connected to the `addSubplotsToFigure` method. + Hides the subvolume points widget and label by default. - def addWidgetstoGridLayout(self, result, param_list, button_text): + Parameters + ---------- + param_list : list of str + A list of parameters to populate the parameter_fix_widget. + button_text : str + The text to display on the button. + """ widgetno=0 self.data_label_label = QLabel(self) @@ -272,7 +328,7 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.grid_layout.addWidget(self.data_label_label,widgetno,1) self.data_label_widget = QComboBox(self) - self.data_label_widget.addItems(result.data_label) + self.data_label_widget.addItems(self.data_label) self.grid_layout.addWidget(self.data_label_widget,widgetno,2) widgetno+=1 @@ -292,8 +348,7 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.grid_layout.addWidget(self.subvol_size_value_label,widgetno,1) self.subvol_size_value_widget = QComboBox(self) - self.subvol_size_value_list = self.subvol_sizes - self.subvol_size_value_widget.addItems(self.subvol_size_value_list) + self.subvol_size_value_widget.addItems(self.subvol_sizes) self.grid_layout.addWidget(self.subvol_size_value_widget,widgetno,2) self.subvol_points_value_label = QLabel(self) @@ -301,21 +356,27 @@ def addWidgetstoGridLayout(self, result, param_list, button_text): self.grid_layout.addWidget(self.subvol_points_value_label,widgetno,1) self.subvol_points_value_widget = QComboBox(self) - self.subvol_points_value_list = self.subvol_points - self.subvol_points_value_widget.addItems(self.subvol_points_value_list) + self.subvol_points_value_widget.addItems(self.subvol_points) self.grid_layout.addWidget(self.subvol_points_value_widget,widgetno,2) - self.showParameterValues(2) - self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.showParameterValues(2)) + self.showParameterValues() + self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.showParameterValues()) widgetno+=1 self.button = QtWidgets.QPushButton(button_text) - self.button.clicked.connect(partial(self.addPlotsToLayout)) + self.button.clicked.connect(partial(self.addSubplotsToFigure)) self.grid_layout.addWidget(self.button,widgetno,2) self.parameter_value_widget_list = [self.subvol_size_value_widget, self.subvol_points_value_widget] - def showParameterValues(self, row): + def showParameterValues(self): + """ + Adjusts the visibility of widgets based on the current index + of the parameter to fix widget. + + The widgets are removed and readded to maintain the spacing of the layout. + """ + layout_row = 2 index = self.parameter_fix_widget.currentIndex() if index == 0: @@ -323,14 +384,14 @@ def showParameterValues(self, row): self.subvol_points_value_widget.hide() self.grid_layout.removeWidget(self.subvol_points_value_label) self.grid_layout.removeWidget(self.subvol_points_value_widget) - self.grid_layout.addWidget(self.subvol_size_value_label, row, 1) - self.grid_layout.addWidget(self.subvol_size_value_widget, row, 2) + self.grid_layout.addWidget(self.subvol_size_value_label, layout_row, 1) + self.grid_layout.addWidget(self.subvol_size_value_widget, layout_row, 2) self.subvol_size_value_label.show() self.subvol_size_value_widget.show() elif index == 1: - self.grid_layout.addWidget(self.subvol_points_value_label, row,1) - self.grid_layout.addWidget(self.subvol_points_value_widget, row,2) + self.grid_layout.addWidget(self.subvol_points_value_label, layout_row,1) + self.grid_layout.addWidget(self.subvol_points_value_widget, layout_row,2) self.subvol_points_value_label.show() self.subvol_points_value_widget.show() self.subvol_size_value_label.hide() @@ -346,18 +407,60 @@ def showParameterValues(self, row): class BulkRunResultsWidget(BulkRunResultsBaseWidget): + ''' + Creates a class widget to plot histograms from several results in a bulk run. + ''' def __init__(self, parent, result_data_frame): + ''' + Initialises the class. Includes the option 'None' to + the parameter list and tailors the text of the button. + ''' param_list = ["Subvolume size", "Sampling points in subvolume", "None"] super().__init__(parent, result_data_frame, param_list, "Plot histograms") + def addSubplotsToFigure(self): + ''' + Clears the current figure, retrieves the selected data label and parameter index. + Constructs the plot title based on the parameter index. + It iterates over the result data frame to create subplots for each result, + adjusting the figure and subplot titles accordingly. The canvas size is adjusted + based on the parameter index and ensures that the subplots are properly spaced and titled. + + The method handles three cases for the parameter index: + - 0: Plots the subplots for all values of the number of points in the subvolume + and fixed value of the subvolume size. + - 1: Plots the subplots for all values of the the subvolume size and fixed number of points in the subvolume. + - 2: Plots the subplots for all values of the subvolume size and number of points in the subvolume. + + Adjusts the figure layout and redraws the canvas. - def addPlotsToLayout(self): - """And stores mean and std""" + Attributes + ---------- + self.fig : matplotlib.figure.Figure + The figure object to which subplots are added. + self.data_label_widget : QComboBox) + Widget to select the data label. + self.parameter_fix_widget : QComboBox + Widget to select the parameter to fix. + self.parameter_value_widget_list : list of QComboBox + List of widgets to select parameter values. + self.run_name : str + Name of the current run. + self.fontsizes : dict + Dictionary containing font sizes for various plot elements. + self.subvol_sizes : list + List of subvolume sizes. + self.subvol_points : list + List of points in subvolumes. + self.result_data_frame : pandas.DataFrame + Data frame containing the results. + self.canvas : matplotlib.backends.backend_qt5agg.FigureCanvasQTAgg + Canvas to draw the figure on. + ''' self.fig.clf() data_label = self.data_label_widget.currentText() param_index = self.parameter_fix_widget.currentIndex() - if param_index == 2: plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} distribution for all parameters" else: @@ -400,25 +503,29 @@ def addPlotsToLayout(self): self._addHistogramSubplot(subplot, row.result_arrays[data_index], x_label, mean, std) subplot.set_title(subplot_title, pad=20, fontsize=self.fontsizes['subplot_title']) self.fig.subplots_adjust(hspace=2,wspace=0.5) + self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() + class StatisticsResultsWidget(BulkRunResultsBaseWidget): + ''' + Creates a class widget to plot statistical analysis from several results in a bulk run. + ''' def __init__(self, parent, result_data_frame): + ''' + Defines the parameter list and linestyles. + Initialises the class. + ''' param_list = ["Subvolume size", "Sampling points in subvolume"] self.linestyles = ['-','--','-.', ':'] super().__init__(parent, result_data_frame, param_list) - - self.subvol_size_value_list = self.subvol_sizes - self.subvol_points_value_list = self.subvol_points - self.subvol_size_value_list = np.append(self.subvol_size_value_list, "All") - self.subvol_points_value_list = np.append(self.subvol_points_value_list, "All") - self.subvol_points_value_widget.addItems(["All"]) - self.subvol_size_value_widget.addItems(["All"]) - - def addWidgetstoGridLayout(self, result, param_list, button_text): + self.subvol_points_value_widget.addItem("All") + self.subvol_size_value_widget.addItem("All") + + def addWidgetstoGridLayout(self, param_list, button_text): "Moves the button one row down and inserts the checkbox before the button" - super().addWidgetstoGridLayout(result, param_list, button_text) + super().addWidgetstoGridLayout(param_list, button_text) self.data_label_widget.addItem("All") widgetno = self.grid_layout.rowCount() - 2 self.collapse_checkbox = QCheckBox("Collapse plots", self) @@ -463,7 +570,7 @@ def hideShowCollapseCheckbox(self): self.collapse_checkbox.hide() - def addPlotsToLayout(self): + def addSubplotsToFigure(self): self.fig.clf() self.canvas.setMinimumSize(800, 400) df = self.result_data_frame @@ -548,7 +655,7 @@ def addPlotsToLayout(self): self.fig.tight_layout(rect=[0, 0, 1, 0.95]) self.canvas.draw() - + class SaveObjectWindow(QtWidgets.QWidget): '''a window which will appear when saving a mask or pointcloud ''' From 52ee645a94e242b99df37e5aa25b2817a8804565 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 2 Oct 2024 17:01:42 +0100 Subject: [PATCH 30/39] Add more docstrings --- src/idvc/ui/widgets.py | 113 ++++++++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 23 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index 7973270c..e0242875 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -36,8 +36,9 @@ def __init__(self, parent, result_data_frame): Parameters ---------- parent : QWidget - result_data_frame : pandas dataframe - Columns: 'subvol_size', 'subvol_points', 'result', 'result_arrays', 'mean_array', 'std_array'. + result_data_frame : pandas.DataFrame + Data frame containing the results, with columns: 'subvol_size', 'subvol_points', + 'result', 'result_arrays', 'mean_array', 'std_array'. ''' self.result_data_frame = result_data_frame single_result = result_data_frame.iloc[0]['result'] @@ -105,8 +106,8 @@ def _selectRow(self, result_data_frame, selected_subvol_points, selected_subvol_ Parameters ---------- - result_data_frame : pandas dataframe - Includes the 'subvol_points' and 'subvol_size' columns. + result_data_frame : pandas.DataFrame + Data frame containing the results. Includes the 'subvol_points' and 'subvol_size' columns. selected_subvol_points : str of an integer Selected points in subvolume. selected_subvol_size : str of an integer @@ -126,8 +127,8 @@ def _selectOneParameter(self, result_data_frame, parameter, selected_parameter): Parameters ---------- - result_data_frame : pandas dataframe - Includes the 'subvol_points' and 'subvol_size' columns. + result_data_frame : pandas.DataFrame + Data frame containing the results. Includes the 'subvol_points' and 'subvol_size' columns. parameter : str 'subvol_points' or 'subvol_size' selected_parameter : str of an integer @@ -145,7 +146,7 @@ def _addHistogramSubplot(self, subplot, array, xlabel, mean, std): Parameters ---------- - plot : matplotlib subplot of a figure + subplot : matplotlib subplot of a figure array : numpyarray xlabel : str mean : float @@ -186,8 +187,8 @@ def __init__(self, parent, result_data_frame): ---------- parent : QWidget The parent widget. - result_data_frame : DataFrame - A pandas DataFrame containing the result data. + result_data_frame : pandas.DataFrame + Data frame containing the result data. ''' super().__init__(parent, result_data_frame) if len(result_data_frame) > 1: @@ -292,8 +293,8 @@ def __init__(self, parent, result_data_frame, param_list, button_text = "Plot"): ---------- parent : QWidget The parent widget. - result_data_frame : DataFrame - A pandas DataFrame containing the result data. + result_data_frame : pandas.DataFrame + Data frame containing the results. param_list : list of str The text items for the QComboBox. button_text : str, optional @@ -515,7 +516,8 @@ class StatisticsResultsWidget(BulkRunResultsBaseWidget): def __init__(self, parent, result_data_frame): ''' Defines the parameter list and linestyles. - Initialises the class. + Initialises the class. Adds the option `All` to the + subvolume size and subvolume points widgets. ''' param_list = ["Subvolume size", "Sampling points in subvolume"] self.linestyles = ['-','--','-.', ':'] @@ -524,7 +526,21 @@ def __init__(self, parent, result_data_frame): self.subvol_size_value_widget.addItem("All") def addWidgetstoGridLayout(self, param_list, button_text): - "Moves the button one row down and inserts the checkbox before the button" + """ + Redefines the superclass method to add widgets to the grid layout. + Adds "All" to the data label widget. + Inserts a checkbox labeled "Collapse plots" into the grid layout and hides it. + Moves the button one row down in the grid layout. + Connects various signals to the `hideShowAllItemInValueWidget` and + `hideShowCollapseCheckbox` methods. + + Parameters + ---------- + param_list : list of str + A list of parameters to populate the parameter_fix_widget. + button_text : str + The text to display on the button. + """ super().addWidgetstoGridLayout(param_list, button_text) self.data_label_widget.addItem("All") widgetno = self.grid_layout.rowCount() - 2 @@ -538,7 +554,41 @@ def addWidgetstoGridLayout(self, param_list, button_text): self.parameter_fix_widget.currentIndexChanged.connect(lambda: self.hideShowCollapseCheckbox()) self.collapse_checkbox.hide() - def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter,x_parameter, x_label, color_mean = '#1f77b4', color_std = '#ff7f0e', label_mean = "Mean", label_std = "Std", linestyle = '-'): + def _meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, data_label, parameter, selected_parameter, x_parameter, x_label, color_mean='#1f77b4', color_std='#ff7f0e', label_mean="Mean", label_std="Std", linestyle='-'): + """ + Plots mean and standard deviation data on given subplots. + + Parameters + ---------- + subplot_mean : matplotlib subplot of a figure + Subplot for mean values. + subplot_std : matplotlib subplot of a figure + Subplot for std values. + result_data_frame : pandas.DataFrame + Data frame containing the results. + data_index : int + Index of the data. + data_label : str + Label for the data. + parameter : str + Parameter to filter the data frame. + selected_parameter : str + Value of the parameter to filter. + x_parameter : str + Column name for x-axis values. + x_label : str + Label for the x-axis. + color_mean : str, optional + Color for mean plot line. + color_std : str, optional + Color for std plot line. + label_mean : str, optional + Label for mean plot line. + label_std : str, optional + Label for std plot line. + linestyle : str, optional + Line style for plot lines. + """ df_sz = self._selectOneParameter(result_data_frame, parameter, selected_parameter) xpoints = df_sz[x_parameter] ypoints = df_sz['mean_array'].apply(lambda array: array[data_index]) @@ -547,6 +597,10 @@ def meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index, self._addStatisticalAnalysisPlot(subplot_std, x_label, data_label+"std", xpoints,ypoints, color_std, label_std, linestyle) def hideShowAllItemInValueWidget(self): + """ + Toggles the visibility of the "All" item in the subvolume size value widget and + subvolume points value widget based on the current text of the data label widget. + """ index_ss = self.subvol_size_value_widget.findText("All") index_sp = self.subvol_points_value_widget.findText("All") if self.data_label_widget.currentText() == "All": @@ -559,6 +613,10 @@ def hideShowAllItemInValueWidget(self): self.subvol_points_value_widget.addItem("All") def hideShowCollapseCheckbox(self): + """ + Shows or hides the collapse checkbox based on the + selection of the fixed parameter. + """ param_index = self.parameter_fix_widget.currentIndex() if param_index == 0: widget = self.subvol_size_value_widget @@ -571,20 +629,29 @@ def hideShowCollapseCheckbox(self): def addSubplotsToFigure(self): + """ + Clears the figure and sets a minimum size for the canvas. + Generates subplots for mean and standard deviation of the selected data + and parameter values. It supports different configurations based on user selections. + The subplots are arranged in a grid layout, where the figure title and subplot titles + are dynamically generated based on the selected options. The figure layout is adjusted + and the canvas is redrawn. + + The method handles the following cases: + - Plotting all data labels with fixed parameter values. + - Plotting a specific data label with fixed parameter values. + - Plotting a specific data label with all parameter values. An option to + collapse the subplots in the same row is available. + """ self.fig.clf() self.canvas.setMinimumSize(800, 400) df = self.result_data_frame - - - param_index = self.parameter_fix_widget.currentIndex() param_text = self.parameter_fix_widget.currentText().lower() x_label = self.parameter_fix_widget.itemText(1 - param_index) value_widget = self.parameter_value_widget_list[param_index] - - self.value_list = [self.subvol_sizes, self.subvol_points] label_list = ['subvol_size','subvol_points'] @@ -600,7 +667,7 @@ def addSubplotsToFigure(self): value = value_widget.currentText() plot_title = f"Bulk run '{self.run_name}': mean and standard deviation for {param_text} = {value}" twin = subplot.twinx() - self.meanStdPlots(subplot, twin, df, data_index, "", label_list[param_index], value, label_list[1-param_index], x_label) + self._meanStdPlots(subplot, twin, df, data_index, "", label_list[param_index], value, label_list[1-param_index], x_label) lines1, labels1 = subplot.get_legend_handles_labels() lines2, labels2 = twin.get_legend_handles_labels() lines = lines1 + lines2 @@ -622,7 +689,7 @@ def addSubplotsToFigure(self): subplot_std = self.fig.add_subplot(numRows, numColumns, plotNum +1) subplot_std.set_title(f"Standard deviation for {param_text} = {value}", fontsize=self.fontsizes['subplot_title'], pad=20) label = f"{param_text} = {value}" - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label, label_mean = label, label_std = label) + self._meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label, label_mean = label, label_std = label) plotNum = plotNum + 2 elif self.collapse_checkbox.isChecked(): numRows = 1 @@ -638,7 +705,7 @@ def addSubplotsToFigure(self): else: color = np.random.rand(3,) label = f"{param_text} = {value}" - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label, color, color, label, label, linestyle = linestyle) + self._meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label, color, color, label, label, linestyle = linestyle) subplot_mean.legend(loc='upper right') subplot_std.legend(loc='upper right') else: @@ -649,7 +716,7 @@ def addSubplotsToFigure(self): subplot_std.set_title("Standard deviation", fontsize=self.fontsizes['subplot_title'], pad=20) value = value_widget.currentText() plot_title = f"Bulk run '{self.run_name}': {data_label.lower()} mean and standard deviation for {param_text} = {value}" - self.meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label) + self._meanStdPlots(subplot_mean, subplot_std, df, data_index, data_label+" ", label_list[param_index], value, label_list[1-param_index], x_label) self.fig.suptitle(plot_title,fontsize=self.fontsizes['figure_title']) self.fig.tight_layout(rect=[0, 0, 1, 0.95]) From dbf8472fe4ae3b558fc68e66779882d153ede9ca Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 2 Oct 2024 17:26:07 +0100 Subject: [PATCH 31/39] Add docstrings to manipulate results files --- src/idvc/utils/manipulate_result_files.py | 75 ++++++++++++++++++++--- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/src/idvc/utils/manipulate_result_files.py b/src/idvc/utils/manipulate_result_files.py index 35fe719a..0a56c0c9 100644 --- a/src/idvc/utils/manipulate_result_files.py +++ b/src/idvc/utils/manipulate_result_files.py @@ -4,24 +4,63 @@ from idvc.utilities import RunResults import glob, os -def extractDataFromDispResultFile(result, displ_wrt_point0): +def _extractDataFromDispResultFile(result, displ_wrt_point0): """ - Gets the filepath of the disp file via `result.disp_file`. - Extracts the data in the numpy format, skips the header. - This imports the whole row, so the displacement vector is given by indices 6, 7, 8. Index 5 is the objective func minimum. - 'plot_data' is a list of array, where each array is a column of data: objmin, u, v, w. + Extracts objective minimum and displacement vectors from a result file. + Optionally, adjusts the displacement vectors relative to the displacement of the first point. + + The objective function minimum is located at index 5. + The displacement vector is extracted from columns indexed 6 to 8 (inclusive). + If `displ_wrt_point0` is True, the displacement values will be adjusted by + subtracting the displacement of the first point. + + Parameters + ---------- + result : RunResults + An object containing the filepath to the displacement result file (`result.disp_file`). + The displacement result file is expected to be tab-delimited and have a header row that will be skipped. + displ_wrt_point0 : bool + If True, the displacement vectors will be adjusted relative to the displacement vector of the first point + (point zero). + + Returns + ------- + numpy.ndarray + 2D array where each row corresponds to a column of data from the file, + including the objective function minimum and displacement vectors. """ data = np.genfromtxt(result.disp_file, delimiter='\t', skip_header=1) data_shape = data.shape index_objmin = 5 - index_disp = [6,9] + index_disp = [6, 9] if displ_wrt_point0: - point0_disp_array = data[0,index_disp[0]:index_disp[1]] - data[:,index_disp[0]:index_disp[1]] = data[:,index_disp[0]:index_disp[1]] - point0_disp_array - result_arrays = np.transpose(data[:,index_objmin:data_shape[1]]) + point0_disp_array = data[0, index_disp[0]:index_disp[1]] + data[:, index_disp[0]:index_disp[1]] -= point0_disp_array + result_arrays = np.transpose(data[:, index_objmin:data_shape[1]]) return result_arrays def createResultsDataFrame(results_folder, displ_wrt_point0): + """ + Creates a pandas DataFrame containing results from DVC result files. + The folder is scanned for subfolders matching the pattern + "dvc_result_*". The data for a result is extracted from each result file, and compiles + the data into a pandas DataFrame. + + Parameters + ---------- + results_folder : str + The path to the folder containing the DVC result subfolders. + displ_wrt_point0 :bool + A flag indicating whether to extract displacement data with respect to point 0. + + Returns + ------- + pd.DataFrame: A DataFrame with the following columns: + - 'subvol_size': List of subvolume sizes as strings. + - 'subvol_points': List of subvolume points as strings. + - 'result': List of RunResults objects. + - 'result_arrays': List of 4 arrays containing extracted data. + """ subvol_size_list = [] subvol_points_list = [] result_list = [] @@ -29,7 +68,7 @@ def createResultsDataFrame(results_folder, displ_wrt_point0): for folder in glob.glob(os.path.join(results_folder, "dvc_result_*")): result = RunResults(folder) - result_arrays = extractDataFromDispResultFile(result, displ_wrt_point0) + result_arrays = _extractDataFromDispResultFile(result, displ_wrt_point0) subvol_size_list.append(str(result.subvol_size)) subvol_points_list.append(str(result.subvol_points)) result_list.append(result) @@ -43,6 +82,22 @@ def createResultsDataFrame(results_folder, displ_wrt_point0): return result_data_frame def addMeanAndStdToResultDataFrame(result_data_frame): + """ + Adds mean and standard deviation arrays to the result DataFrame. + + In particular, iterates over each row in the DataFrame, calculates the mean and + standard deviation for each array in the 'result_arrays' column, and appends + the new columns 'mean_array' and 'std_array'. + + Parameters + ---------- + result_data_frame : pd.DataFrame + A pandas DataFrame. + + Returns + ------- + pd.DataFrame: The modified DataFrame with additional columns 'mean_array' and 'std_array'. + """ mean_array_list = [] std_array_list = [] for row in result_data_frame.itertuples(): From 88618bcefb5017aac74294d52e7d0377f7ce95cc Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 2 Oct 2024 17:50:55 +0100 Subject: [PATCH 32/39] Edit help text --- src/idvc/dvc_interface.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/idvc/dvc_interface.py b/src/idvc/dvc_interface.py index 98b3b078..e1d8ccf1 100644 --- a/src/idvc/dvc_interface.py +++ b/src/idvc/dvc_interface.py @@ -487,13 +487,21 @@ def CreateHelpPanel(self): "4. Limit the range of the vectors viewed by changing the 'Vector Range Min' and Vector Range Max'. Then, click 'View Pointcloud/Vectors' to apply the changes.\n" "5. On the 2D viewer, the vectors are shown as 2D arrows, showing the displacements in the current plane. If the 'x', 'y' or 'z' keys are pressed click 'View Pointcloud/Vectors' to apply the changes.\n\n" "Display Graphs:\n" - "Graphs are displayed in a new window (once you are done looking at the graphs you can either close or minimize this window). A tab is created for each run and an additional tab for the bulk run.\n" - "1. Select an option from the list for the variable to compare.\n" - "2. Select the parameters to compare from the list.\n" - "3. Click on 'Plot Histograms'.\n" - "Note: This will automatically show the displacements including the translation that you set in the manual registration.\n" - "4. Optionally, go to 'Settings' and select 'Show displacement relative to reference point 0' to adjust the displacements to exclude the initial registration translation.\n" - "5. In the case of a bulk run, a particular variable can be selected and the graphs for this variable in each of the runs can be compared.\n\n" + "Graphs are displayed in a new window (once you are done looking at the graphs you can either close or minimize this window).\n" + "A tab is created to visualise a single run. Information on the run is displayed on the left.\n" + "1. Select an option for the result to plot.\n" + "2. Select the parameter to fix and its value.\n" + "3. Click on 'Plot'.\n" + "In the case of a bulk run, an additional tab enables comparison of the results. Information on the bulk run is displayed on the left.\n" + "1. Select an option for the result to plot.\n" + "2. Select the parameter to fix and its value. Alternatively, select 'None' to plot all values.\n" + "3. Click on 'Plot histograms'.\n" + "An additional tab includes quantatitative statistical analysis of the bulk run.\n" + "1. Select an option for the result to plot. Alternatively, select 'All'.\n" + "2. Select the parameter to fix and its value. Alternatively, select 'All' to plot all values. Optionally, collapse the plots.\n" + "3. Click on 'Plot'.\n" + "Note: As a default, the displacements include the translation set in the manual registration.\n" + "4. Optionally, go to 'Settings' and select 'Show displacement relative to reference point 0' to adjust the displacements to exclude the initial registration translation.\n\n" "Results Files:\n" "Select a folder and export a session to access the result files. Two tab-delimited text files are generated for each run at location \Results\\dvc_result_*.\n" "1. The status file (dvc_result_*.stat) contains an echo of the input file used for the analysis, information about the point cloud, dvc program version, run date/time, search statistics and timing.\n" From f6cf877cc349271bc6d923824ae3fa76344e2bb8 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Wed, 2 Oct 2024 18:11:04 +0100 Subject: [PATCH 33/39] Use template font --- src/idvc/idvc.py | 3 ++- src/idvc/ui/dialogs.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index 994a4bde..a879fb20 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -18,7 +18,8 @@ def main(): logging.info(f"iDVC: Setting debugging level to {args.debug.upper()}") app = QtWidgets.QApplication([]) # Set a global font for the application - font = QtGui.QFont("Arial", 12) # Replace with your preferred font and size + default_font_family = app.font().family() + font = QtGui.QFont(default_font_family, 12) # Replace with your preferred font and size QtWidgets.QApplication.setFont(font) diff --git a/src/idvc/ui/dialogs.py b/src/idvc/ui/dialogs.py index 4f590d0e..1fdb1563 100644 --- a/src/idvc/ui/dialogs.py +++ b/src/idvc/ui/dialogs.py @@ -111,7 +111,8 @@ def __init__(self, parent, title="Settings"): def onOk(self): - font = PySide2.QtGui.QFont("Arial", self.fontsize_widget.value()) + default_font_family = PySide2.QtWidgets.QApplication.font().family() + font = PySide2.QtGui.QFont(default_font_family, self.fontsize_widget.value()) PySide2.QtWidgets.QApplication.setFont(font) #self.parent.settings.setValue("settings_chosen", 1) if self.dark_checkbox.isChecked(): From e70bd13fba394b68f37b7a35669bd0ac5178fe77 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 7 Oct 2024 10:16:22 +0100 Subject: [PATCH 34/39] Improve docstring --- src/idvc/ui/widgets.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/idvc/ui/widgets.py b/src/idvc/ui/widgets.py index e0242875..a38d2c4c 100644 --- a/src/idvc/ui/widgets.py +++ b/src/idvc/ui/widgets.py @@ -139,7 +139,7 @@ def _selectOneParameter(self, result_data_frame, parameter, selected_parameter): return df def _addHistogramSubplot(self, subplot, array, xlabel, mean, std): - '''Given an array, calculates the relative counts by using the mtplotlib + '''Given an array, calculates the relative counts by using the matplotlib histogram functionality. It clears the plot and plots the relative frequency histogram as a bar plot. Sets the x an y labels. Adds the mean and std values as vertical lines. Plots the gaussian fit. Adds the legend to the plot. @@ -168,7 +168,7 @@ def _addHistogramSubplot(self, subplot, array, xlabel, mean, std): subplot.legend(loc='upper right') def _addStatisticalAnalysisPlot(self, subplot, xlabel, ylabel, xpoints, ypoints, color, label, linestyle): - "Draws a line plot in subplot. Adds labels and sets user defined properties." + "Draws a line plot in 'subplot'. Adds labels and sets user-defined properties." subplot.plot(xpoints, ypoints, color=color, linestyle=linestyle, linewidth=self.linewidth, label=label) subplot.set_ylabel(ylabel + " (pixels)", fontsize=self.fontsizes['label']) subplot.set_xlabel(xlabel, fontsize=self.fontsizes['label']) @@ -188,7 +188,7 @@ def __init__(self, parent, result_data_frame): parent : QWidget The parent widget. result_data_frame : pandas.DataFrame - Data frame containing the result data. + Dataframe containing the result data. ''' super().__init__(parent, result_data_frame) if len(result_data_frame) > 1: @@ -231,7 +231,7 @@ def addSubplotsToFigure(self): ''' Clears the current figure. Determines the number of rows and columns for the figure layout. Selects the appropriate row - from the result data frame based on the user selected subvolume points and size. + from the result dataframe based on the user selected subvolume points and size. Extracts result arrays, mean array, and standard deviation array from the selected row. Sets the figure title with details about the run and subvolume. Iterates over the result arrays to create histograms and adds them as subplots. @@ -301,7 +301,6 @@ def __init__(self, parent, result_data_frame, param_list, button_text = "Plot"): The text to display on the QPushButton. ''' super().__init__(parent, result_data_frame) - single_result = result_data_frame.iloc[0]['result'] self.addWidgetstoGridLayout(param_list, button_text) self.addSubplotsToFigure() @@ -373,7 +372,7 @@ def addWidgetstoGridLayout(self, param_list, button_text): def showParameterValues(self): """ Adjusts the visibility of widgets based on the current index - of the parameter to fix widget. + of the parameter-to-fix widget. The widgets are removed and readded to maintain the spacing of the layout. """ @@ -598,8 +597,8 @@ def _meanStdPlots(self, subplot_mean, subplot_std, result_data_frame, data_index def hideShowAllItemInValueWidget(self): """ - Toggles the visibility of the "All" item in the subvolume size value widget and - subvolume points value widget based on the current text of the data label widget. + Toggles the visibility of the "All" item in the subvolume-size-value widget and + subvolume-points-value widget based on the current text of the data-label widget. """ index_ss = self.subvol_size_value_widget.findText("All") index_sp = self.subvol_points_value_widget.findText("All") From 81ebb56035ce18b1a88bbc59f009ee1aa1a26bb7 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 7 Oct 2024 10:52:10 +0100 Subject: [PATCH 35/39] Add docstrings to windows --- src/idvc/ui/windows.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/idvc/ui/windows.py b/src/idvc/ui/windows.py index 76d080de..b1f70845 100644 --- a/src/idvc/ui/windows.py +++ b/src/idvc/ui/windows.py @@ -178,11 +178,16 @@ def consumeCharEvent(self, interactor, event): interactor.SetKeyCode("") class GraphsWindow(QMainWindow): - '''creates a new window with graphs from results saved in the selected run folder. - ''' + """ + Creates a new window with graphs from results saved in the selected run folder. + """ def __init__(self, parent=None): + """ + Initialises the class. Adds an icon to the window. + Adds a menu with the option to show displacement relative to point 0. + Sets the position of the tabs in the window and the geometry of the window. + """ super(GraphsWindow, self).__init__(parent) - self.setWindowTitle("Digital Volume Correlation Results") DVCIcon = QtGui.QIcon() DVCIcon.addFile("DVCIconSquare.png") @@ -209,19 +214,35 @@ def __init__(self, parent=None): #self.setFixedSize(geometry.width() * 0.6, geometry.height() * 0.8) def SetResultsFolder(self, folder): + """Creates the attribute 'result_folder' and sets the graphs-window title.""" self.results_folder = folder self.setWindowTitle("Run {foldername}".format(foldername=os.path.basename(self.results_folder))) def ReloadGraphs(self): + """Deletes all widgets in the graphs window and creates new ones.""" self.DeleteAllWidgets() self.CreateDockWidgets(displ_wrt_point0 = self.displacement_setting_action.isChecked()) def DeleteAllWidgets(self): + """Deletes all dock widgets in the graphs window.""" for current_dock in self.findChildren(QDockWidget): current_dock.close() del current_dock def CreateDockWidgets(self, displ_wrt_point0 = False): + """ + Creates and configures dock widgets for displaying results. + + Initialises and adds dock widgets to the graphs window. + It creates a single-run results widget and, if there are multiple results, + a bulk-run results widget and a statistical-analysis widget. + The widgets are added to the right dock widget area and tabified. + + Parameters + ---------- + displ_wrt_point0 : bool, optional + A flag to indicate whether to display results with respect to point 0. + """ result_data_frame = createResultsDataFrame(self.results_folder, displ_wrt_point0) result_data_frame = addMeanAndStdToResultDataFrame(result_data_frame) single_run_results_widget = SingleRunResultsWidget(self, result_data_frame) From c2c16422b602a07d819100fc989c6840ca60f170 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 7 Oct 2024 11:19:52 +0100 Subject: [PATCH 36/39] Edit changelog --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 88695d74..55b4f28d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,13 @@ # ChangeLog +## v24.1.0 +Enhancements: +* Improve graphs window #335 +* Option to edit the app fontsize in settings #335 + +Dependencies: +* Added pandas #335 + ## v24.0.1 Bug fixes: * Use RawInputDialog from the viewer package #314 From 2ef5c529f0b756456e9910262c98b2fb6b62195f Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Mon, 7 Oct 2024 12:06:28 +0100 Subject: [PATCH 37/39] Edit changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 55b4f28d..a1a9ba46 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Enhancements: * Option to edit the app fontsize in settings #335 Dependencies: -* Added pandas #335 +* Add pandas #335 ## v24.0.1 Bug fixes: From 6cd93ce28b0f81ad22f95b4036a21b4646daeac3 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 8 Oct 2024 14:54:50 +0100 Subject: [PATCH 38/39] Edit results file --- docs/source/results.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/results.rst b/docs/source/results.rst index 9bb20120..1254e58f 100644 --- a/docs/source/results.rst +++ b/docs/source/results.rst @@ -10,14 +10,14 @@ Graphs of the Results ===================== Then click on **Display Graphs**. Another window will open (once you are done looking at the graphs you can either close or minimize this window and it will take you back to the main app just fine). -It will start you off on the **Summary** tab. +It will start you off on the **Bulk** tab. This isn’t so useful if you only performed one run. For each run that you performed, there will be a separate tab. If you navigate to one of these it will show you graphs for the objective minimum, and displacements in x, y, z as well as changes in φ, θ, ψ for that run. The title of the tab also gives the number of sampling points in the subvolume and the subvolume size. This will automatically show the displacements including the translation that you set in the manual registration. You can adjust the displacements to exclude this translation by going to Settings and selecting **Show displacement relative to reference point 0**. -Now, coming back to the summary tab, this shows the settings for the runs including the subvolume geometry, maximum displacement etc., +Now, coming back to the bulk tab, this shows the settings for the runs including the subvolume geometry, maximum displacement etc., and if you have done a bulk run then you can select a particular variable (such as the objective minimum) and then compare the graphs for this variable in each of the runs. You can select to just compare for a certain subvolume size or number of sampling points, or you can choose to compare them all (which is what is chosen in the image below). From fba4fb19ab0e647b4e6be2048e1b6f5faa5c1644 Mon Sep 17 00:00:00 2001 From: Danica Sugic Date: Tue, 8 Oct 2024 14:56:10 +0100 Subject: [PATCH 39/39] Delete unused imports --- src/idvc/idvc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/idvc/idvc.py b/src/idvc/idvc.py index a879fb20..ee9f168c 100644 --- a/src/idvc/idvc.py +++ b/src/idvc/idvc.py @@ -1,5 +1,4 @@ -import PySide2 -from PySide2 import QtWidgets, QtGui, QtCore +from PySide2 import QtWidgets, QtGui import os, sys import logging import argparse