diff --git a/plotting_refactor/DataCollector.py b/plotting_refactor/DataCollector.py
new file mode 100644
index 0000000000..90d08fe889
--- /dev/null
+++ b/plotting_refactor/DataCollector.py
@@ -0,0 +1,118 @@
+import RandomDatasetCreator
+from Dataset import Dataset
+from PySide6 import QtWidgets
+
+
+class DataCollector:
+ """
+ This class keeps track of all generated datasets. When the update_dataset is called through onCalculate
+ from MainWindow, either a new dataset can be created or the existing dataset is adjusted with respect to
+ the currently displayed SpinBox Values from the Fitpage.
+ """
+ def __init__(self):
+ self._datasets: list[Dataset] = []
+ self.datasetcreator = RandomDatasetCreator.DatasetCreator()
+
+ def update_dataset(self, main_window: QtWidgets.QMainWindow, fitpage_index: int,
+ create_fit: bool, checked_2d: bool):
+ """
+ Search for an existing dataset saved in here. If no dataset for the corresponding fitpage exists: create new
+ data.
+ """
+
+ # search for an existing dataset with the right fitpage_index
+ existing_dataset_index = -1
+ for i, dataset in enumerate(self._datasets):
+ if dataset.fitpage_index == fitpage_index:
+ existing_dataset_index = i
+
+ if existing_dataset_index == -1:
+ # create new dataset in case it does not exist
+ x_data, y_data, y_fit = self.simulate_data(main_window, create_fit, checked_2d)
+ plotpage_index = -1
+
+ dataset = Dataset(fitpage_index, x_data, y_data, y_fit, checked_2d, plotpage_index)
+ self._datasets.append(dataset)
+ else:
+ # update values for existing dataset with respect to the number boxes in the fitpage
+ x_data, y_data, y_fit = self.simulate_data(main_window, create_fit, checked_2d)
+ self._datasets[existing_dataset_index].x_data = x_data
+ self._datasets[existing_dataset_index].y_data = y_data
+ self._datasets[existing_dataset_index].y_fit = y_fit
+ self._datasets[existing_dataset_index].is_data_2d = checked_2d
+
+ def simulate_data(self, main_window: QtWidgets.QMainWindow, create_fit: bool, checked_2d: bool):
+ """
+ Collect all information from the FitPage from the MainWindow hat is needed to calculate the test data.
+ Feed this information to the DatasetCreator and return the values.
+ """
+ combobox_index = main_window.fittingTabs.currentWidget().get_combobox_index()
+ param_scale = main_window.fittingTabs.currentWidget().doubleSpinBox_scale.value()
+ param_radius = main_window.fittingTabs.currentWidget().doubleSpinBox_radius.value()
+ param_height = main_window.fittingTabs.currentWidget().doubleSpinBox_height.value()
+
+ x_data, y_data, y_fit = self.datasetcreator.createRandomDataset(param_scale, param_radius, param_height,
+ combobox_index, create_fit, checked_2d)
+
+ return x_data, y_data, y_fit
+
+ @property
+ def datasets(self) -> list:
+ return self._datasets
+
+ def find_object_by_property(self, obj_list: list, property_name: str, property_value: int):
+ for obj in obj_list:
+ if hasattr(obj, property_name) and getattr(obj, property_name) == property_value:
+ return obj
+
+ return None
+
+ def get_data_by_fp(self, fitpage_index: int) -> Dataset:
+ """
+ Get the dataset for a certain fitpage
+ """
+ return self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+
+ def get_data_by_id(self, data_id: int) -> Dataset:
+ """
+ Get the dataset for certain id
+ """
+ return self.find_object_by_property(self._datasets, "data_id", data_id)
+
+ def get_x_data(self, fitpage_index: int) -> list:
+ """
+ Get x data for certain fitpage index
+ """
+ dataset = self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+ return dataset.x_data
+
+ def get_y_data(self, fitpage_index: int) -> list:
+ """
+ Get y data for certain fitpage index
+ """
+ dataset = self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+ return dataset.y_data
+
+ def get_y_fit_data(self, fitpage_index: int) -> list:
+ """
+ Get y fit data for certain fitpage index
+ """
+ dataset = self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+ return dataset.y_fit
+
+ def get_plotpage_index(self, fitpage_index: int) -> int:
+ """
+ Get the plotpage index for a certain fitpage index: Plotpage index refers to the index of the major tabs in
+ the plotting widget in which the data is displayed.
+ """
+ dataset = self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+ return dataset.plotpage_index
+
+ def set_plot_index(self, fitpage_index: int, plot_index: int):
+ """
+ Set the plotpage index for the dataset for a certain fitpage index. Plotpage index refers to the index of the
+ major tabs in the plotting widget in which the data is displayed.
+ """
+ dataset = self.find_object_by_property(self._datasets, "fitpage_index", fitpage_index)
+ dataset.plotpage_index = plot_index
+
diff --git a/plotting_refactor/DataTreeItems.py b/plotting_refactor/DataTreeItems.py
new file mode 100644
index 0000000000..b83e70539b
--- /dev/null
+++ b/plotting_refactor/DataTreeItems.py
@@ -0,0 +1,29 @@
+from PySide6.QtWidgets import QTreeWidgetItem
+
+
+class PlotPageItem(QTreeWidgetItem):
+ def __init__(self, parent, name, fitpage_index, data_id):
+ super().__init__(parent, name)
+ self._fitpage_index = fitpage_index
+ self._data_id = data_id
+ super().setData(0, 1, self)
+
+ @property
+ def fitpage_index(self):
+ return self._fitpage_index
+
+ @property
+ def data_id(self):
+ return self._data_id
+
+class DataItem(PlotPageItem):
+ def __init__(self, parent, name, fitpage_index, data_id, type_num):
+ super().__init__(parent, name, fitpage_index, data_id)
+ # self.type_num saves if the item is a data item or a fit item in the tree widget
+ # identifier=1 is for data and identifier=2 is for fit identifier=3 is for residuals
+ self._type_num = type_num
+
+ @property
+ def type_num(self):
+ return self._type_num
+
diff --git a/plotting_refactor/DataTreeWidget.py b/plotting_refactor/DataTreeWidget.py
new file mode 100644
index 0000000000..8db6bf3a59
--- /dev/null
+++ b/plotting_refactor/DataTreeWidget.py
@@ -0,0 +1,35 @@
+from DataTreeItems import DataItem
+from PySide6.QtCore import QByteArray, QMimeData, QRect, Qt
+from PySide6.QtGui import QDrag
+from PySide6.QtWidgets import QTreeWidget
+
+
+class DataTreeWidget(QTreeWidget):
+ """
+ Tree widget that is appearing in the DataViewer. It represents data stored in the DataCollector by objects from
+ DataTreeItems. Instantiating of these DataTreeItems happens in the DataViewer.
+ """
+ def __init__(self, dataviewer, datacollector):
+ super().__init__(parent=dataviewer)
+ self.datacollector = datacollector
+ self.setGeometry(QRect(10, 10, 391, 312))
+ self.setDragEnabled(True)
+ self.setColumnCount(1)
+ self.setHeaderLabels(["Data Name"])
+
+ def startDrag(self, supportedActions: Qt.DropAction):
+ """
+ Overwriting the startDrag from the normal QTreeWidget. When dragging the QTreeWidgetItem to another plot,
+ mimetypes ID and Type are used to store the dataset.data_id and the type_num. Type_num represents if the
+ item is a data, fit or residuals item.
+ """
+ item = self.currentItem()
+ if item:
+ if isinstance(item.data(0, 1), DataItem):
+ drag = QDrag(self)
+ mimeData = QMimeData()
+ mimeData.setData('ID', QByteArray(str(item.data(0, 1).data_id)))
+ mimeData.setData('Type', QByteArray(str(item.data(0, 1).type_num)))
+
+ drag.setMimeData(mimeData)
+ drag.exec(supportedActions)
diff --git a/plotting_refactor/DataViewer.py b/plotting_refactor/DataViewer.py
new file mode 100644
index 0000000000..2f3c7ff590
--- /dev/null
+++ b/plotting_refactor/DataViewer.py
@@ -0,0 +1,226 @@
+from DataCollector import DataCollector
+from DataTreeItems import DataItem, PlotPageItem
+from DataTreeWidget import DataTreeWidget
+from PlotModifiers import ModifierColormap, ModifierLinecolor, ModifierLinestyle
+from PlotTreeItems import PlotItem, PlottableItem, SubTabItem, TabItem
+from PlotTreeWidget import PlotTreeWidget
+from PlotWidget import PlotWidget
+from PySide6 import QtWidgets
+from UI.DataViewerUI import Ui_DataViewer
+
+
+class DataViewer(QtWidgets.QWidget, Ui_DataViewer):
+ """
+ Class for interface between Plotwidget and Datacollector. Processing of signals for plotting,
+ redrawing of existing plots, adding new plot modifiers ends here.
+ """
+ def __init__(self, main_window):
+ """
+ Main Window is used as a parameter in the constructor to be able to hand it further to the Datacollector which
+ can then directly read values from checkboxes and spinboxes for new model calculations.
+
+ self.dataTreeWidget and self.plotTreeWidget represent data that exists in the DataCollector or existing plots
+ in the plot widget, respectively.
+ """
+ super(DataViewer, self).__init__()
+ self.setupUi(self)
+
+ self.main_window = main_window
+ self.datacollector = DataCollector()
+
+ self.dataTreeWidget = DataTreeWidget(self, self.datacollector)
+ self.plotTreeWidget = PlotTreeWidget(self)
+
+ self.cmdClose.clicked.connect(self.onShowDataViewer)
+ self.cmdAddModifier.clicked.connect(self.onAddModifier)
+ self.plotTreeWidget.dropSignal.connect(self.redraw)
+
+ self.setupMofifierCombobox()
+ self.plot_widget = PlotWidget(self, self.datacollector)
+
+ def create_plot(self, fitpage_index):
+ self.update_plot_tree(fitpage_index)
+ self.plot_widget.show()
+ self.plot_widget.activateWindow()
+
+ def update_datasets_from_collector(self, fitpage_index: int):
+ """
+ Collects datasets from the datacollector and adds them to the dataTreeWidget. Is called upon a plot or
+ calculation request from the mainwindow. Only adds the dataset with the corresponding fitpage_index.
+ """
+ datasets = self.datacollector.datasets
+ already_exists = False
+ for i in range(self.dataTreeWidget.topLevelItemCount()):
+ if fitpage_index == self.dataTreeWidget.topLevelItem(i).data(0, 1).fitpage_index:
+ already_exists = True
+
+ if not already_exists:
+ for dataset in datasets:
+ if fitpage_index == dataset.fitpage_index:
+ name = "Data from Fitpage " + str(fitpage_index)
+ data_id = dataset.data_id
+ item = PlotPageItem(self.dataTreeWidget, [name], fitpage_index, data_id)
+ item.setData(0, 1, item)
+ subitem_data = DataItem(item, ["Data"], fitpage_index, data_id, 1)
+ subitem_data.setData(0, 1, subitem_data)
+ if dataset.has_y_fit():
+ subitem_fit = DataItem(item, ["Fit"], fitpage_index, data_id, 2)
+ subitem_fit.setData(0, 1, subitem_fit)
+
+ self.dataTreeWidget.expandAll()
+
+ def onShowDataViewer(self):
+ """
+ Function for handling showing and hiding of the data viewer and the button for that in the main window
+ """
+ if self.isVisible():
+ self.hide()
+ self.main_window.cmdShowDataViewer.setText("Show Data Viewer")
+ else:
+ self.show()
+ self.main_window.cmdShowDataViewer.setText("Hide Data Viewer")
+
+ def update_dataset(self, fitpage_index, create_fit, checked_2d):
+ """
+ Updates existing or non-existing datasets in the datacollector for a fitpage in the mainwindow
+ """
+ self.datacollector.update_dataset(self.main_window, fitpage_index, create_fit, checked_2d)
+ self.update_datasets_from_collector(fitpage_index)
+
+ def update_plot_tree(self, fitpage_index):
+ """
+ Function to populate the plotTreeWidget for a certain fitpage. Checks if a plot for the given fitpage already
+ exists and recreates it if so. Therefore it collects all the data for the given fitpage from the datacollector
+ and creates the Tabs, Subtabs, Plots, Plottables for the plotTreeWidget. This mechanism also checks, if a
+ dataitem that comes from the datacollector is 2d. If it is 2d, the type_num for this PlottableItem will be
+ different (4 instead of 1) and the SubTabs.py can recognize, that only this 2d data can be plotted in one
+ actual plot.
+ """
+ # check if an item for the fitpage index already exists
+ # if one is found - remove from tree
+ for i in range(self.plotTreeWidget.topLevelItemCount()):
+ if isinstance(self.plotTreeWidget.topLevelItem(i), TabItem):
+ if fitpage_index == self.plotTreeWidget.topLevelItem(i).data(0, 1).fitpage_index:
+ self.plotTreeWidget.takeTopLevelItem(i)
+
+ # add tab
+ tab_name = "Plot for Fitpage " + str(fitpage_index)
+ tab_item = TabItem(self.plotTreeWidget, [tab_name], fitpage_index)
+ tab_item.setData(0, 1, tab_item)
+
+ # add data child and corresponding plot children in every case
+ subtab_data = SubTabItem(tab_item, ["Data"], fitpage_index, 0)
+ subplot_data = PlotItem(subtab_data, ["Data Plot"], fitpage_index, 0, 0,
+ self.datacollector.get_data_by_fp(fitpage_index).is_data_2d)
+ fitpage_id = self.datacollector.get_data_by_fp(fitpage_index).data_id
+
+ # create plottables in the plottreewidget with indicators (type_nums) to identify what kind of plot it is while
+ # plotting in subtabs.py: type_num = 1 : 1d data, type_num = 2 : 1d fit, type_num = 3 : 1d residuals
+ # type_num = 4 : 2d data, type_num = 5 : 2d fit, type_num = 6 : 2d residuals
+ # 2d plots cannot overlap each other as curves can do
+ # for every 2d data an additional plot is added and 1 plottable is inserted
+ if self.datacollector.get_data_by_fp(fitpage_index).is_data_2d:
+ plottable_data = PlottableItem(subplot_data, ["2d " + str(fitpage_id)], fitpage_id, 4)
+ else:
+ plottable_data = PlottableItem(subplot_data, [str(fitpage_id)], fitpage_id, 1)
+
+ #add fit and residuals in case it was generated
+ if self.datacollector.get_data_by_fp(fitpage_index).has_y_fit():
+ # on the fit tab: one central plot that shows the dataset and the according fit curve
+ # create tab for fit and residual plot
+ subtab_fit = SubTabItem(tab_item, ["Fit"], fitpage_index, 1)
+ subtab_residuals = SubTabItem(tab_item, ["Residuals"], fitpage_index, 2)
+ # if the data is 2d, then every plot contains only one plottable
+ if self.datacollector.get_data_by_fp(fitpage_index).is_data_2d:
+ subplot_data_subtab_fit = PlotItem(subtab_fit, ["Data"], fitpage_index, 1, 0, True)
+ plottable_subplot_data_subtab_fit = PlottableItem(subplot_data_subtab_fit, ["2d Plottable Fit Data"], fitpage_id, 4)
+
+ subplot_fit_subtab_fit = PlotItem(subtab_fit, ["Fit"], fitpage_index, 1, 1, True)
+ plottable_subplot_fit_subtab_fit = PlottableItem(subplot_fit_subtab_fit, ["2d Plottable Fit Fit"], fitpage_id, 5)
+
+
+ subplot_data_subtab_residuals = PlotItem(subtab_residuals, ["Data"], fitpage_index, 2, 0, True)
+ plottable_subplot_data_subtab_residuals = PlottableItem(subplot_data_subtab_residuals, ["2d Plottable Residuals Data"], fitpage_id, 4)
+
+ subplot_fit_subtab_residuals = PlotItem(subtab_residuals, ["Fit"], fitpage_index, 2, 1, True)
+ plottable_subplot_fit_subtab_residuals = PlottableItem(subplot_fit_subtab_residuals, ["2d Plottable Residuals Fit"], fitpage_id, 5)
+
+ subplot_residuals_subtab_residuals = PlotItem(subtab_residuals, ["Residuals"], fitpage_index, 2, 2, True)
+ plottable_subplot_residuals_subtab_residuals = PlottableItem(subplot_residuals_subtab_residuals, ["2d Plottable Residuals Residuals"], fitpage_id, 6)
+
+ else: # if the data is 1d, multiple plottables can be plotted in one plot
+ subplot_fit = PlotItem(subtab_fit, ["Fit Plot"], fitpage_index, 1, 0, False)
+ plottable_fit_data = PlottableItem(subplot_fit, ["Plottable Fit Data"], fitpage_id, 1)
+ plottable_fit_fit = PlottableItem(subplot_fit, ["Plottable Fit Fit"], fitpage_id, 2)
+
+ # on the residuals subtab: create 2 plots with 3 datasets: on the top plot is the data and the fit,
+ # on the bottom plot is the residuals displayed with the same x-axis for comparison
+ subplot_residuals_fit = PlotItem(subtab_residuals, ["Fit Plot"], fitpage_index, 2, 0, False)
+ plottable_res_data = PlottableItem(subplot_residuals_fit, ["Plottable Res Data"], fitpage_id, 1)
+ plottable_res_fit = PlottableItem(subplot_residuals_fit, ["Plottable Res Fit"], fitpage_id, 2)
+
+ subplot_res = PlotItem(subtab_residuals, ["Residuals Plot"], fitpage_index, 2, 1, False)
+ plottable_res = PlottableItem(subplot_res, ["Plottable Residuals"], fitpage_id, 3)
+
+ self.plotTreeWidget.expandAll()
+ self.redraw(fitpage_index, 0)
+
+ def redraw(self, redraw_fitpage_index, redraw_subtab_index):
+ """
+ Redraws all tabs in the plotTreeWidget. parameters redraw_fitpage_index and redraw_subtab_index are used to show
+ the subtab for which the redrawAll was invoked, because a modifier was dragged onto a child plot or plottable
+ item in the plotTreeWidget.
+ If redrawing is invoked from the update_plot_tree method, only the fitpage_index will be used but 0 for
+ the subplot.
+ """
+ if self.plotTreeWidget.topLevelItemCount() != 0:
+ for i in range(self.plotTreeWidget.topLevelItemCount()):
+ if isinstance(self.plotTreeWidget.topLevelItem(i).data(0, 1), TabItem):
+ self.plot_widget.redrawTab(self.plotTreeWidget.topLevelItem(i))
+
+ plotpage_index = self.datacollector.get_plotpage_index(redraw_fitpage_index)
+ self.plot_widget.setCurrentIndex(plotpage_index)
+ self.plot_widget.widget(plotpage_index).setCurrentIndex(redraw_subtab_index)
+
+ def remove_plottree_item(self, index: int):
+ """
+ Remove toplevelitem from plottreeitem upon closing a tab in the plottreewidget.
+ """
+ # search for the existing dataset with the right plotpage index
+ datasets = self.datacollector.datasets
+ for dataset in datasets:
+ if dataset.plotpage_index == index:
+ fitpage_index_tab = dataset.fitpage_index
+
+ # look through the toplevel items for the item with the right fitpage_index, that needs to be deleted.
+ for i in range(self.plotTreeWidget.topLevelItemCount()):
+ if self.plotTreeWidget.topLevelItem(i).data(0, 1).fitpage_index == fitpage_index_tab:
+ self.plotTreeWidget.takeTopLevelItem(i)
+
+
+ def onAddModifier(self):
+ """
+ Add modifiers via button press to the plotTreeWidget. These can then be dragged around on PlotItems and
+ PlottableItems. Logic for dragging is in the PlotTreeWidget.py.
+ """
+ currentmodifier = self.comboBoxModifier.currentText()
+ if 'color' in currentmodifier:
+ mod = ModifierLinecolor(self.plotTreeWidget, [currentmodifier])
+ if 'linestyle' in currentmodifier:
+ mod = ModifierLinestyle(self.plotTreeWidget, [currentmodifier])
+ if 'scheme' in currentmodifier:
+ mod = ModifierColormap(self.plotTreeWidget, [currentmodifier])
+ def setupMofifierCombobox(self):
+ """
+ Gives all the different available modifiers to the combobox so that they can be created by user selection.
+ """
+ self.comboBoxModifier.addItem("color=r")
+ self.comboBoxModifier.addItem("color=g")
+ self.comboBoxModifier.addItem("color=b")
+ self.comboBoxModifier.addItem("linestyle=solid")
+ self.comboBoxModifier.addItem("linestyle=dashed")
+ self.comboBoxModifier.addItem("linestyle=dotted")
+ self.comboBoxModifier.addItem("scheme=jet")
+ self.comboBoxModifier.addItem("scheme=spring")
+ self.comboBoxModifier.addItem("scheme=gray")
+
diff --git a/plotting_refactor/Dataset.py b/plotting_refactor/Dataset.py
new file mode 100644
index 0000000000..ea70ae20f0
--- /dev/null
+++ b/plotting_refactor/Dataset.py
@@ -0,0 +1,81 @@
+import time
+
+
+class Dataset:
+ """
+ Generic dataset class to hold all of the generated data for one fitpage with its generated id in one place.
+ The generated id is a timestamp with the fitpage number that the dataset belongs to as a prefix.
+ """
+ def __init__(self, fitpage_index: int, x_data: list, y_data: list, y_fit: list, is_data_2d: list[list],
+ plotpage_index: int = 0):
+ self._fitpage_index = fitpage_index
+ self._x_data = x_data
+ self._y_data = y_data
+ self._y_fit = y_fit
+ self._plotpage_index = plotpage_index
+ self._is_data_2d = is_data_2d
+ self._data_id = self.__generate_id(self._fitpage_index)
+
+ def __generate_id(self, fitpage_index: int):
+ a = str(int(time.time()))
+ b = len(a)
+ new_id = int(str(fitpage_index) + a[5:b])
+ return new_id
+
+ @property
+ def data_id(self) -> int:
+ return self._data_id
+
+ @property
+ def fitpage_index(self) -> int:
+ return self._fitpage_index
+
+ @property
+ def x_data(self) -> list:
+ return self._x_data
+
+ @property
+ def y_data(self) -> list:
+ return self._y_data
+
+ @property
+ def y_fit(self) -> list:
+ return self._y_fit
+
+ def has_y_fit(self) -> bool:
+ if self._y_fit.size == 0:
+ return False
+ else:
+ return True
+
+ @property
+ def plotpage_index(self) -> int:
+ return self._plotpage_index
+
+ @plotpage_index.setter
+ def plotpage_index(self, plotpage_index: int):
+ if isinstance(plotpage_index, int):
+ self._plotpage_index = plotpage_index
+ else:
+ print("no integer")
+
+ @x_data.setter
+ def x_data(self, x_data: list):
+ self._x_data = x_data
+
+ @y_data.setter
+ def y_data(self, y_data: list):
+ self._y_data = y_data
+
+ @y_fit.setter
+ def y_fit(self, y_fit: list):
+ self._y_fit = y_fit
+
+ @property
+ def is_data_2d(self) -> bool:
+ return self._is_data_2d
+
+ @is_data_2d.setter
+ def is_data_2d(self, is_data_2d: bool):
+ self._is_data_2d = is_data_2d
+
diff --git a/plotting_refactor/FitPage.py b/plotting_refactor/FitPage.py
new file mode 100644
index 0000000000..c875a8a59f
--- /dev/null
+++ b/plotting_refactor/FitPage.py
@@ -0,0 +1,38 @@
+from PySide6 import QtWidgets
+from UI.FitPageUI import Ui_fitPageWidget
+
+
+class FitPage(QtWidgets.QWidget, Ui_fitPageWidget):
+ """
+ Widget that is shown in the tabs from the Mainwindow. Is a subclass of a widget to directly store fitpage indexes
+ in it.
+ """
+ def __init__(self, identifier: int):
+ super(FitPage, self).__init__()
+ self.setupUi(self)
+
+ #identifier keeps track of which number this fitpage is identifier by (it is incremental)
+ self._identifier = identifier
+
+ self.comboBoxFormFactor.addItems(["Sphere", "Cylinder"])
+ self.doubleSpinBox_height.setDisabled(True)
+ self.comboBoxFormFactor.currentIndexChanged.connect(self.index_changed)
+
+ @property
+ def identifier(self) -> int:
+ return self._identifier
+
+ def get_combobox_index(self) -> int:
+ return self.comboBoxFormFactor.currentIndex()
+
+ def get_checkbox_fit(self) -> bool:
+ return self.checkBoxCreateFit.isChecked()
+
+ def get_checkbox_2d(self) -> bool:
+ return self.checkBox2dData.isChecked()
+
+ def index_changed(self, selected_item: int):
+ if selected_item == 0:
+ self.doubleSpinBox_height.setDisabled(True)
+ elif selected_item == 1:
+ self.doubleSpinBox_height.setDisabled(False)
diff --git a/plotting_refactor/MainWindow.py b/plotting_refactor/MainWindow.py
new file mode 100644
index 0000000000..95520a411b
--- /dev/null
+++ b/plotting_refactor/MainWindow.py
@@ -0,0 +1,83 @@
+import sys
+import traceback
+
+from DataViewer import DataViewer
+from FitPage import FitPage
+from PySide6 import QtWidgets
+from UI.MainWindowUI import Ui_MainWindow
+
+
+class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
+ """
+ MainWindow for the application, uses self.fittingTabs to create a tab selection of FitPages in which
+ comboboxes and spinboxes are placed for data creation. Also has calculation and plot buttons to invoke methods
+ for these logics.
+ Owner of the DataViewer, that centralizes the logic between plotting and data handling (with the DataCollector)
+ """
+ def __init__(self):
+ super(MainWindow, self).__init__()
+ self.setupUi(self)
+
+ self.setWindowTitle("Tabbed Plot Demo")
+ self.setFixedSize(700, 560)
+
+ self.fitPageCounter = 1
+ self.fittingTabs.addTab(FitPage(self.fitPageCounter), "Fit Page "+str(self.fitPageCounter))
+
+ self.dataviewer = DataViewer(self)
+
+ self.cmdShowDataViewer.clicked.connect(self.dataviewer.onShowDataViewer)
+ self.cmdPlot.clicked.connect(self.onPlot)
+ self.cmdCalculate.clicked.connect(self.onCalculate)
+ self.actionNewFitPage.triggered.connect(self.onActionNewFitPage)
+
+ def onPlot(self):
+ """
+ Invoked when pressing plot button, collects the fitpage_index for the currently selected fitpage and gives it
+ to other parts of the program where this is used as a unique identifier for datasets that are saved in the
+ DataCollector.
+ Invokes plot creation after data creation.
+ """
+ fitpage_index = self.fittingTabs.currentWidget().identifier
+ self.onCalculate()
+ self.dataviewer.create_plot(fitpage_index)
+
+ def onCalculate(self):
+ """
+ Calculates data for the currently selected fitpage. This data is then shown in the DataViewer dataTreeWidget.
+ """
+ fitpage_index = self.fittingTabs.currentWidget().identifier
+ create_fit = self.fittingTabs.currentWidget().get_checkbox_fit()
+ checked_2d = self.fittingTabs.currentWidget().get_checkbox_2d()
+ self.dataviewer.update_dataset(fitpage_index, create_fit, checked_2d)
+
+ def onActionNewFitPage(self):
+ """
+ Creates a new fitpage by the button in the menubar of the mainwindow on top.
+ """
+ self.fitPageCounter += 1
+ self.fittingTabs.addTab(FitPage(self.fitPageCounter), "Fit Page " + str(self.fitPageCounter))
+ self.fittingTabs.setCurrentIndex(self.fitPageCounter-1)
+
+ def closeEvent(self, event):
+ QtWidgets.QApplication.closeAllWindows()
+ sys.exit()
+
+def excepthook(exc_type, exc_value, exc_tb):
+ tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
+ print("error caught!:")
+ print("error message:\n", tb)
+ QtWidgets.QApplication.quit()
+
+
+def main():
+ sys.excepthook = excepthook
+ app = QtWidgets.QApplication(sys.argv)
+ window = MainWindow()
+ window.show()
+
+ ret = app.exec()
+ sys.exit(ret)
+
+if __name__ == '__main__':
+ main()
diff --git a/plotting_refactor/PlotModifiers.py b/plotting_refactor/PlotModifiers.py
new file mode 100644
index 0000000000..a1583cddd7
--- /dev/null
+++ b/plotting_refactor/PlotModifiers.py
@@ -0,0 +1,32 @@
+from PySide6.QtWidgets import QTreeWidgetItem
+
+
+class PlotModifier(QTreeWidgetItem):
+ def __init__(self, parent, name):
+ super().__init__(parent, name)
+ self.setData(0, 1, self)
+
+class ModifierLinestyle(PlotModifier):
+ def __init__(self, parent, name):
+ super().__init__(parent, name)
+
+ def clone(self):
+ copy = super().clone()
+ return ModifierLinestyle(copy.parent(), [copy.text(0)])
+
+class ModifierLinecolor(PlotModifier):
+ def __init__(self, parent, name):
+ super().__init__(parent, name)
+
+ def clone(self):
+ copy = super().clone()
+ return ModifierLinecolor(copy.parent(), [copy.text(0)])
+
+
+class ModifierColormap(PlotModifier):
+ def __init__(self, parent, name):
+ super().__init__(parent, name)
+
+ def clone(self):
+ copy = super().clone()
+ return ModifierColormap(copy.parent(), [copy.text(0)])
diff --git a/plotting_refactor/PlotTreeItems.py b/plotting_refactor/PlotTreeItems.py
new file mode 100644
index 0000000000..329b75c878
--- /dev/null
+++ b/plotting_refactor/PlotTreeItems.py
@@ -0,0 +1,77 @@
+from PySide6.QtWidgets import QTreeWidgetItem
+
+
+class TabItem(QTreeWidgetItem):
+ """
+ Class for representation in the PlotTreeWidget. Saves the fitpage index to know, which data needs to be plotted
+ in the redrawing process of this tab.
+ """
+ def __init__(self, parent, name, fitpage_index):
+ super().__init__(parent, name)
+ self._fitpage_index = fitpage_index
+ super().setData(0, 1, self)
+
+ @property
+ def fitpage_index(self):
+ return self._fitpage_index
+
+class SubTabItem(TabItem):
+ """
+ Class for representation in the PlotTreeWidget. Has both fitpage index (from the parent TabItem) and subtab_index
+ for plotting purposes in the redrawing process.
+ """
+ def __init__(self, parent, name, fitpage_index, subtab_index):
+ super().__init__(parent, name, fitpage_index)
+ self._subtab_index = subtab_index
+
+ @property
+ def subtab_index(self):
+ return self._subtab_index
+
+class PlotItem(SubTabItem):
+ """
+ Class for representation in the PlotTreeWidget. Has fitpage_index and subtab_index from the parent items. _ax_index
+ and _is_plot_2d class attributes are used when the PlotTreeWidget item is drawn for redrawing.
+ """
+ def __init__(self, parent, name, fitpage_index, subtab_index, ax_index, is_plot_2d):
+ super().__init__(parent, name, fitpage_index, subtab_index)
+ self._ax_index = ax_index
+ self._is_plot_2d = is_plot_2d
+
+ @property
+ def ax_index(self):
+ return self._ax_index
+
+ @property
+ def is_plot_2d(self):
+ return self._is_plot_2d
+
+class PlottableItem(QTreeWidgetItem):
+ """
+ Class for representation in the PlotTreeWidget. Has _data_id and _type_num for replotting purposes in the redrawing
+ process.
+ """
+ def __init__(self, parent, name, data_id, type_num):
+ super().__init__(parent, name)
+ self._data_id = data_id
+ # type serves the same purpose as in DataTreeItems - knowing if the item is a data item or a fit item, so that
+ # for example the axes can be scaled accordingly
+ self._type_num = type_num
+ super().setData(0, 1, self)
+
+ @property
+ def data_id(self):
+ return self._data_id
+
+ @property
+ def type_num(self):
+ # type_num = 1: 1d data,
+ # type_num = 2: 1d fit,
+ # type_num = 3: 1d residuals,
+ # type_num = 4 : 2d data,
+ # type_num = 5 : 2d fit,
+ # type_num = 6 : 2d residuals
+ return self._type_num
+
+
+
diff --git a/plotting_refactor/PlotTreeWidget.py b/plotting_refactor/PlotTreeWidget.py
new file mode 100644
index 0000000000..69f1fd9044
--- /dev/null
+++ b/plotting_refactor/PlotTreeWidget.py
@@ -0,0 +1,129 @@
+import ctypes
+
+from PlotModifiers import PlotModifier
+from PlotTreeItems import PlotItem, PlottableItem
+from PySide6.QtCore import QByteArray, QMimeData, QRect, Signal
+from PySide6.QtGui import QDrag
+from PySide6.QtWidgets import QTreeWidget
+
+
+class PlotTreeWidget(QTreeWidget):
+ dropSignal = Signal(int, int)
+ def __init__(self, DataViewer):
+ super().__init__(parent=DataViewer)
+ self.setGeometry(QRect(10, 332, 391, 312))
+ self.setDragEnabled(True)
+ self.setAcceptDrops(True)
+ self.setDropIndicatorShown(True)
+ self.setColumnCount(1)
+ self.setHeaderLabels(["Plot Names"])
+
+ def startDrag(self, supportedActions):
+ """
+ Function for starting the drag of modifiers across the PlotTreeWidget.
+ """
+ item = self.currentItem()
+
+ if item:
+ if isinstance(item, PlotModifier):
+ drag = QDrag(self)
+ mimeData = QMimeData()
+ mimeData.setData('Modifier', QByteArray(str(id(item))))
+
+ drag.setMimeData(mimeData)
+ drag.exec(supportedActions)
+
+ def dragEnterEvent(self, event):
+ event.acceptProposedAction()
+
+
+ def dragMoveEvent(self, event):
+ """
+ Function for checking if a drop should be accepted or not. DataItems from the DataTreeWidget are allowed and
+ the drop is accepted, if they are dropped onto a plot item.
+ Drops of modifiers are allowed if the modifier is dragged onto a PlotItem or a PlottableItem
+ TODO: This needs some change, since 1d modifiers should only be droppable onto Plottables and 2d only on Plots.
+ """
+ targetItem = self.itemAt(event.position().toPoint())
+ if targetItem is not None:
+ if event.mimeData().hasFormat('ID'):
+ if isinstance(targetItem, PlotItem):
+ event.acceptProposedAction()
+ else:
+ event.ignore()
+ elif event.mimeData().hasFormat('Modifier'):
+ if isinstance(targetItem, PlotItem):
+ event.acceptProposedAction()
+ elif isinstance(targetItem, PlottableItem):
+ event.acceptProposedAction()
+ else:
+ event.ignore()
+ else:
+ event.ignore()
+ else:
+ event.ignore()
+
+ def dropEvent(self, event):
+ """
+ Processing of the drop of either a modifier from this widget or a data item from the DataTreeWidget.
+ """
+
+ # Check, if the drag item contains "ID", then we can be sure that this comes from the DataTreeWidget
+ # (this might not be best practice, but with the serialization of the pointer as it is now, is seemed to me
+ # like it was the easiest method to achieve this)
+ if event.mimeData().data('ID'):
+
+ # Re-collect the data from these streams in the drop
+ data_id = event.mimeData().data('ID').data()
+ data_type = event.mimeData().data('Type').data()
+
+ # get the targetItem from the drop position
+ targetItem = self.itemAt(event.position().toPoint())
+
+ # if the target is a PlotItem, we deserialize the address of the pointer and create a new item from the
+ # object that is behind the pointer.
+ if isinstance(targetItem.data(0, 1), PlotItem):
+ new_plottable = PlottableItem(targetItem, [str(data_id.decode('utf-8'))],
+ int(data_id), int(data_type))
+
+ # get the fitpage index and the subtab index of the targetItem, so that they can be activated upon redrawing
+ redraw_fitpage_index = targetItem.data(0, 1).fitpage_index
+ redraw_subtab_index = targetItem.data(0, 1).subtab_index
+
+ elif isinstance(targetItem.data(0, 1), PlottableItem):
+ # as soon as plottable slots are there, they can be filled in here
+ pass
+
+ # the drop signal can be emitted now so that the tab where something was dragged onto can be redrawn.
+ self.dropSignal.emit(redraw_fitpage_index, redraw_subtab_index)
+ event.acceptProposedAction()
+
+ # Here, the serialization also plays a role, because the modifier is cloned in the process and used to create
+ # a new child that the modifier will be for the target item.
+ elif event.mimeData().data('Modifier'):
+ data_address = int(event.mimeData().data('Modifier').data())
+ data = ctypes.cast(data_address, ctypes.py_object).value
+ clone = data.clone()
+ targetItem = self.itemAt(event.position().toPoint())
+
+ if isinstance(targetItem.data(0, 1), PlottableItem):
+ targetItem.addChild(clone)
+ redraw_fitpage_index = targetItem.parent().data(0, 1).fitpage_index
+ redraw_subtab_index = targetItem.parent().data(0, 1).subtab_index
+
+ elif isinstance(targetItem.data(0, 1), PlotItem):
+ targetItem.addChild(clone)
+ redraw_fitpage_index = targetItem.data(0, 1).fitpage_index
+ redraw_subtab_index = targetItem.data(0, 1).subtab_index
+
+ self.dropSignal.emit(redraw_fitpage_index, redraw_subtab_index)
+ event.acceptProposedAction()
+
+ else:
+ event.ignore()
+
+
+
+
+
+
diff --git a/plotting_refactor/PlotWidget.py b/plotting_refactor/PlotWidget.py
new file mode 100644
index 0000000000..3c3029fe34
--- /dev/null
+++ b/plotting_refactor/PlotWidget.py
@@ -0,0 +1,56 @@
+from PySide6.QtWidgets import QTabWidget
+from SubTabs import SubTabs
+
+
+class PlotWidget(QTabWidget):
+ def __init__(self, dataviewer, datacollector):
+ super().__init__()
+ self.setWindowTitle("Plot Widget")
+ self.setMinimumSize(600, 600)
+
+ self.setTabsClosable(True)
+
+ self.dataviewer = dataviewer
+ self.datacollector = datacollector
+
+ self.tabCloseRequested.connect(self.closeTab)
+
+ def closeTab(self, index: int):
+ """
+ Action that is executed, when a tab is closed in the plotwidget.
+ """
+ self.removeTab(index)
+
+ self.dataviewer.remove_plottree_item(index)
+
+ def widget(self, index) -> SubTabs:
+ return super().widget(index)
+
+ def redrawTab(self, tabitem):
+ # check if the tab is already existing.
+ # if it is not existing: create it. otherwise: recalculate the tab
+ fitpage_index = tabitem.fitpage_index
+ plot_index = self.datacollector.get_data_by_fp(fitpage_index).plotpage_index
+ if plot_index == -1:
+ self.datacollector.set_plot_index(fitpage_index, self.count())
+ self.addTab(SubTabs(self.datacollector, tabitem),
+ "Plot for FitPage " + str(fitpage_index))
+ else:
+ self.removeTab(plot_index)
+ self.insertTab(plot_index, SubTabs(self.datacollector, tabitem),
+ "Plot for FitPage " + str(fitpage_index))
+
+ def get_subtabs(self, fitpage_index):
+ for i in range(self.count()):
+ if fitpage_index == self.widget(i).fitpage_index:
+ return self.widget(i)
+
+ def get_figures(self, fitpage_index):
+ for i in range(self.count()):
+ if fitpage_index == self.widget(i).fitpage_index:
+ return self.widget(i).figures
+
+
+
+
+
diff --git a/plotting_refactor/RandomDatasetCreator.py b/plotting_refactor/RandomDatasetCreator.py
new file mode 100644
index 0000000000..91ec0230ba
--- /dev/null
+++ b/plotting_refactor/RandomDatasetCreator.py
@@ -0,0 +1,67 @@
+import numpy as np
+from scipy import special
+from scipy.optimize import curve_fit
+
+
+class DatasetCreator:
+ """
+ Class to generate data that can be used by the demo plots.
+ self.combobox_index remains a class variable, because then the fitting algorithm can use the same method in
+ differrent cases.
+ """
+ def __init__(self):
+ self.combobox_index = -1
+
+ def func_2d(self, q, scale, radius, height):
+ """
+ add 2d function that represents 2d "simulated" data in the 2d plots
+ """
+ x = self.func(q[0], scale, radius, height)
+ y = self.func(q[1], scale, radius, height)
+ z = np.matmul(x, y)
+ return z
+
+ def func(self, q, scale, radius, height):
+ """
+ function for generating data with either spherical functions (case self.combobox_index== 0) or bessel functions
+ (case self.combobox_index == 1)
+ """
+ if self.combobox_index == 0:
+ volume = 4 / 3 * np.pi * radius**3
+ return scale / volume * (3*volume*(np.sin(q*radius)-q*radius*np.cos(q*radius)) / (q*radius)**3)**2
+ elif self.combobox_index == 1:
+ volume = height * np.pi * radius**2
+ return 4 * scale * volume * (special.jv(1, q*radius))**2 / (q*radius)**2
+
+ def createRandomDataset(self, scale, radius, height, combobox_index, fit=False, second_dimension=False):
+ """
+ Creates a dataset with x, y and y_fit values, that use a 1d fitting algorithm. errors are applied to the data.
+ """
+ self.combobox_index = combobox_index
+ size = 100
+ intensity_fit = np.array([])
+ if second_dimension:
+ q = np.linspace(start=1, stop=10, num=size).reshape(size, 1)
+ q_vec = (q, q.T)
+ intensity_no_err = self.func_2d(q_vec, scale, radius, height)
+ err = intensity_no_err * (np.random.random() - 0.5) * 2
+ intensity = intensity_no_err + err
+ if fit:
+ intensity_1d = intensity.reshape(size*size)
+ q_1d = np.tile(q, size)
+ q_stack = np.vstack((q_1d, q_1d))
+ p_opt, p_cov = curve_fit(f=self.func_2d, xdata=q_stack, ydata=intensity_1d, p0=(1.5, 1.5, 1.5))
+ intensity_fit = self.func_2d(q_vec, *p_opt)
+ q = np.meshgrid(q, q)
+
+ else:
+ q = np.linspace(start=1, stop=10, num=size)
+ intensity = np.empty(shape=size)
+ intensity_no_err = self.func(q, scale, radius, height)
+ err = (np.random.random(size)-0.5) * intensity_no_err
+
+ intensity = intensity_no_err + err
+ if fit:
+ p_opt, p_cov = curve_fit(f=self.func, xdata=q, ydata=intensity, p0=(1.5, 1.5, 1.5))
+ intensity_fit = self.func(q, *p_opt)
+ return q, intensity, intensity_fit
diff --git a/plotting_refactor/SubTabs.py b/plotting_refactor/SubTabs.py
new file mode 100644
index 0000000000..0a91759a0e
--- /dev/null
+++ b/plotting_refactor/SubTabs.py
@@ -0,0 +1,191 @@
+
+import matplotlib.figure
+import numpy as np
+from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg, NavigationToolbar2QT
+from PlotModifiers import ModifierColormap, ModifierLinecolor, ModifierLinestyle, PlotModifier
+from PlotTreeItems import PlottableItem
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QDockWidget, QMainWindow, QTabWidget, QVBoxLayout, QWidget
+
+
+class ClickableCanvas(FigureCanvasQTAgg):
+ """
+ This class provides an extension of the normal Qt Figure Canvas, so that clicks on subplots of a figure can be
+ processed to switch the plot position. Example: if there are 3 plots in a figure 1,2,3 and plot 3 is clicked,
+ the clicked plot will always change its position with the plot 1.
+ """
+ def __init__(self, figure):
+ super().__init__(figure)
+ self.mpl_connect("button_press_event", self.onclick)
+ self.big = 0
+
+ def onclick(self, event):
+ big = self.big
+ if event.inaxes:
+ axs = self.figure.get_axes()
+ for index, ax in enumerate(axs):
+ if (index != big) and (ax == event.inaxes):
+ temp = axs[big].get_position()
+ axs[big].set_position(axs[index].get_position())
+ axs[index].set_position(temp)
+ self.big = index
+ self.draw()
+
+class SubTabs(QTabWidget):
+ """
+ Class for keeping subtabs and adding figures with subplots to them. It takes a tabitem to process and iterates
+ over all the existing children of the given tabitem to plot their contents in the respective order. For example
+ for every child item of the TabItem, one subtab will be created and for every child item of the subtab, one plot
+ will be generated.
+ The application of modifiers onto plots is also managed in this class constructor.
+ """
+ def grayOutOnDock(self, dock_container: QMainWindow, dock_widget: QDockWidget):
+ """
+ Function that is connected to the topLevelChanged slot of the dock widget for the plot widget. When the
+ dock is floating, the area where the dock widget was before, is grayed out. When it is docked in again,
+ the state is reverted.
+ """
+ name = dock_container.objectName()
+ if dock_widget.isFloating():
+ print("gray")
+ dock_container.setStyleSheet("QMainWindow#" + name + " { background-color: gray }")
+ else:
+ print("white")
+ dock_container.setStyleSheet("QMainWindow#" + name + " { background-color: white }")
+
+ def __init__(self, datacollector, tabitem):
+ super().__init__()
+
+ self.datacollector = datacollector
+ self.figures: list[matplotlib.figure] = []
+ # iterate through subtabs
+ for i in range(tabitem.childCount()):
+ # add subplots
+ layout = QVBoxLayout()
+ figure = matplotlib.figure.Figure(figsize=(5, 5))
+ canvas = ClickableCanvas(figure)
+ layout.addWidget(canvas)
+ layout.addWidget(NavigationToolbar2QT(canvas))
+
+ # decide whether there is only one plot needs to be plotted. then, only one central plot is needed
+ subplot_count = tabitem.child(i).childCount()
+ if subplot_count == 1:
+ ax = figure.subplots(subplot_count)
+ # putting the axes object in a list so that the access can be generic for both cases with multiple
+ # subplots and without
+ ax = [ax]
+ else:
+ # for multiple subplots: decide on the ratios for the bigger, central plot and the smaller, side plots
+ # region for the big central plot in gridspec
+ gridspec = figure.add_gridspec(ncols=2, width_ratios=[3, 1])
+ # region for the small side plots in sub_gridspec
+ sub_gridspec = gridspec[1].subgridspec(ncols=1, nrows=subplot_count - 1)
+
+ ax = [figure.add_subplot(gridspec[0])]
+ # add small plots to axes list, so it can be accessed that way
+ for idx in range(subplot_count-1):
+ ax.append(figure.add_subplot(sub_gridspec[idx]))
+
+ # after the subplots are created, the axes objects need to be filled with actual lines/2d plots
+ # iterate through subplots
+ for j in range(tabitem.child(i).childCount()):
+ # set the title of the plot with the subplot name of the PlotTreeWidget item
+ ax[j].set_title(str(tabitem.child(i).child(j).text(0)))
+
+ # iterate through plottables and plot modifiers (PlotTreeWidget items)
+ for k in range(tabitem.child(i).child(j).childCount()):
+
+ plottable_or_modifier_item = tabitem.child(i).child(j).child(k).data(0, 1)
+ # check if the plottable or modifier item is a PlottableItem (actual data to be displayed)
+ if isinstance(plottable_or_modifier_item, PlottableItem):
+ plottable = plottable_or_modifier_item
+ dataset = self.datacollector.get_data_by_id(plottable.data_id)
+
+ # if the dataset is 2d, plotting will be done with a heatmap plot
+ if dataset.is_data_2d:
+
+ # collect a possible existing colormap plot modifier (child item)
+ # and save it, so that it can be used during plot creation
+ colormap_modifier = ""
+ for ii in range(plottable.childCount()):
+ if isinstance(plottable.child(ii), ModifierColormap):
+ colormap_modifier = plottable.child(ii).text(0).split('=')[1]
+ if colormap_modifier == "":
+ colormap_modifier = "jet"
+
+ # get the data from the dataset for the plot
+ x = dataset.x_data
+ y = dataset.y_data
+ y_fit = dataset.y_fit
+
+ # check if the plot is a data plot (4), fit plot (5) or residual plot (6)
+ if plottable.type_num == 4:
+ ax[j].pcolor(x[0], x[1], y,
+ norm=matplotlib.colors.LogNorm(vmin=np.min(y),
+ vmax=np.max(y)),
+ cmap=colormap_modifier)
+ elif plottable.type_num == 5:
+ ax[j].pcolor(x[0], x[1], y_fit,
+ norm=matplotlib.colors.LogNorm(vmin=np.min(y_fit),
+ vmax=np.max(y_fit)),
+ cmap=colormap_modifier)
+ elif plottable.type_num == 6:
+ y_res = np.absolute(np.subtract(y_fit, y))
+ ax[j].pcolor(x[0], x[1], y_res,
+ norm=matplotlib.colors.LogNorm(vmin=np.min(y_res),
+ vmax=np.max(y_res)),
+ cmap=colormap_modifier)
+
+ # if it is not a 2d plot, it must be a 1d plot (line plot)
+ else:
+ # select again for data plot (1), fit plot (2) and residual plot (3)
+ if plottable.type_num == 1: # data plot: log-log plot, show only data
+ ax[j].plot(dataset.x_data, dataset.y_data)
+ ax[j].set_yscale('log')
+ elif plottable.type_num == 2: # fit plot: log-log plot, show fit and data curve
+ ax[j].plot(dataset.x_data, dataset.y_fit)
+ ax[j].set_yscale('log')
+ elif plottable.type_num == 3: # residual plot lin-log plot, show calc and show res only
+ ax[j].plot(dataset.x_data, np.subtract(dataset.y_fit, dataset.y_data))
+
+ # iterate through plottable modifier, e.g. linecolor, linestyle
+ for l in range(plottable.childCount()):
+ plottable_modifier = plottable.child(l)
+ if isinstance(plottable_modifier.data(0, 1), ModifierLinecolor):
+ ax[j].get_lines()[-1].set_color(plottable_modifier.text(0).split('=')[1])
+ elif isinstance(plottable_modifier.data(0, 1), ModifierLinestyle):
+ ax[j].get_lines()[-1].set_linestyle(plottable_modifier.text(0).split('=')[1])
+
+ # applying a colormap to a set of lines and setting the respective color to lines that are
+ # returned by the axes object
+ elif isinstance(plottable_or_modifier_item, PlotModifier):
+ plot_modifier = plottable_or_modifier_item
+ if isinstance(plot_modifier, ModifierColormap):
+ cmap = matplotlib.colormaps[plot_modifier.text(0).split('=')[1]]
+ n = len(ax[j].get_lines())
+ for m in range(n):
+ ax[j].get_lines()[m].set_color(cmap(m/(n-1)))
+
+ # create the widget that will be inside the dock widget
+ figure.tight_layout()
+ canvas_widget = QWidget()
+ canvas_widget.setLayout(layout)
+
+ # create the main window, which is the container for the dock widget, so that it can be dragged out and
+ # put in again
+ dock_container = QMainWindow()
+ # set the object name for later, so that the style sheet changes for graying out only affects the dock
+ # container itself and not the child widgets of the dock container. fitpage_index is used as an identifier
+ # here
+ dock_container.setObjectName("DockContainer" + str(tabitem.data(0, 1).fitpage_index))
+ dock_widget = QDockWidget()
+
+ dock_widget.topLevelChanged.connect(lambda x: self.grayOutOnDock(dock_container, dock_widget))
+
+ dock_widget.setWidget(canvas_widget)
+ dock_container.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea, dock_widget)
+
+ self.addTab(dock_container, tabitem.child(i).text(0))
+ self.figures.append(figure)
+
+
diff --git a/plotting_refactor/UI/DataViewerUI.ui b/plotting_refactor/UI/DataViewerUI.ui
new file mode 100644
index 0000000000..aee94e7002
--- /dev/null
+++ b/plotting_refactor/UI/DataViewerUI.ui
@@ -0,0 +1,67 @@
+
+
+ DataViewer
+
+
+
+ 0
+ 0
+ 700
+ 680
+
+
+
+
+ 700
+ 680
+
+
+
+
+ 700
+ 680
+
+
+
+ Data Viewer
+
+
+
+
+ 620
+ 650
+ 75
+ 24
+
+
+
+ Close
+
+
+
+
+
+ 10
+ 650
+ 241
+ 22
+
+
+
+
+
+
+ 260
+ 650
+ 91
+ 24
+
+
+
+ Add Modifier
+
+
+
+
+
+
diff --git a/plotting_refactor/UI/FitPageUI.ui b/plotting_refactor/UI/FitPageUI.ui
new file mode 100644
index 0000000000..413f347d8a
--- /dev/null
+++ b/plotting_refactor/UI/FitPageUI.ui
@@ -0,0 +1,164 @@
+
+
+ fitPageWidget
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Form
+
+
+
+
+ 80
+ 100
+ 62
+ 22
+
+
+
+ 0.000000000000000
+
+
+ 1000.000000000000000
+
+
+ 1.000000000000000
+
+
+
+
+
+ 80
+ 130
+ 62
+ 22
+
+
+
+ 0.000000000000000
+
+
+ 1000.000000000000000
+
+
+ 1.000000000000000
+
+
+
+
+
+ 20
+ 100
+ 51
+ 16
+
+
+
+ Scale
+
+
+
+
+
+ 20
+ 130
+ 51
+ 16
+
+
+
+ Radius
+
+
+
+
+
+ 20
+ 70
+ 211
+ 16
+
+
+
+ Parameters for scattering function P(q)
+
+
+
+
+
+ 20
+ 20
+ 131
+ 22
+
+
+
+
+
+
+ 80
+ 160
+ 62
+ 22
+
+
+
+ 0.000000000000000
+
+
+ 1000.000000000000000
+
+
+ 1.000000000000000
+
+
+
+
+
+ 20
+ 160
+ 51
+ 16
+
+
+
+ Height
+
+
+
+
+
+ 210
+ 20
+ 181
+ 20
+
+
+
+ Create Fit for generated Data
+
+
+
+
+
+ 210
+ 40
+ 171
+ 20
+
+
+
+ Generate 2D data
+
+
+
+
+
+
diff --git a/plotting_refactor/UI/MainWindowUI.ui b/plotting_refactor/UI/MainWindowUI.ui
new file mode 100644
index 0000000000..8ea013bd5b
--- /dev/null
+++ b/plotting_refactor/UI/MainWindowUI.ui
@@ -0,0 +1,96 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 700
+ 560
+
+
+
+ MainWindow
+
+
+
+
+
+ 500
+ 490
+ 91
+ 24
+
+
+
+ Calculate
+
+
+
+
+
+ 10
+ 10
+ 681
+ 471
+
+
+
+ -1
+
+
+
+
+
+ 10
+ 490
+ 111
+ 24
+
+
+
+ Show Data Viewer
+
+
+
+
+
+ 600
+ 490
+ 91
+ 24
+
+
+
+ Plot
+
+
+
+
+
+
+
+ New Fit Page
+
+
+
+
+
+
diff --git a/plotting_refactor/__init__.py b/plotting_refactor/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/plotting_refactor/notes_and_sketches/Agenda.txt b/plotting_refactor/notes_and_sketches/Agenda.txt
new file mode 100644
index 0000000000..662616c309
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/Agenda.txt
@@ -0,0 +1,122 @@
+Possible Todos for the demo: (I will not put effort into realizing these, just ideas and corrections)
+1 Residuals for a fit need to be plotted beneath the Fit and therefore the grid structure of the plotting needs change
+2 Real data linking could be something to look into (right now, there is only the identifier)
+3 Dragging modifiers across the PlotTreeWidget causes trouble, because it does not autoscroll for whatever reason
+ This could maybe be fixed with using a debugger
+ It is possible to start the application normally and connect a debugger to the running instance afterwards
+4 Delete button for modifiers
+
+Current working branch for the integration of the demo into SasView is: plotting_refactor_integration
+This branch (plotting_refactor_tabs) is with a standalone example in the plotting_refactor folder
+ To start the demo, plotting_refactor/MainWindow.py needs to be executed
+
+
+Other todos that were mentioned and could need integration in SasView in general:
+The button for killing a Fitpage is way too small when executing SasView in a remote windows/linux instance
+ Is there the possibility to have a prompt if the current Fitpage should really be closed?
+ The showing of this prompt could be enabled/disabled in the settings
+
+Lionel had a request with integration of a functionality similar to this:
+https://github.com/criosx/molgroups/tree/main
+
+
+Information regarding SasView that helps to understand the plotting process a little better:
+PlotterWidget extends PlotterBase (PlotterWidget is a class in the file Plotter.py)
+PlotterBase extends QWidget (uses for example QWidget.show or QWidget.showNormal)
+PlotterBase gets its manager from PlotterWidget
+PlotterWidget is instantiated in DataExplorerWindow method plotData (ll.1205)
+DataExplorerWindow is given as the parent and the manager to the PlotterWidget
+DataExplorerWindow has the MainWindow as its parent, the GuiManager as its guimanager and the GuiManager._data_manager as its manager
+
+GuiManager.communicate is an instance of GuiUtils.Communicate()
+GuiUtils.Communicate is a utility class for tracking the Qt Signals and connecting all of them to the right end
+GuiUtils.Communicate extends QtCore.QObject. This class defines all the signals that are needed and processed (ll.83f.)
+Signals are connected to everything in GuiManager.addCallbacks which executed when instantiating the GuiManager
+ in the start of the SasView MainWindow (MainWindow.py)
+
+
+Short overview on the plotting process:
+What happens when clicking the "Plot" button in the FittingWidget in Sasview?
+(1) FittingWidget.py -> onPlot -> showPlot/showTheoryPlot -> _requestPlots -> goes to tabbedPlotWidget among other things
+ -> throws Signal to GuiManager
+(2) Signal for Guimanager.showPlotFromName or Guimanager.showPlot redirects to
+ to DataExplorerWidget.displayData or DataExplorerWidget.displayDataByName
+(3) displayData ends in either active_plots.showNormal(4.1) or plotData(4.2) or updatePlot(4.3) or appendOrUpdatePlot(4.4)
+(4.1) active_plots.showNormal is just a QWidget method that shows the Widget in its restored original size
+(4.2) plotData creates a PlotterWidget instance and sets the item of it to the given item of the method (DataExplorer ll.1205)
+(4.3) updatePlot goes to PlotterWidget.replacePlot:
+ This updates the bookkeeping dictionary self.active_plots, where all the PlotterWidget items that are currently
+ needed are saved
+(4.4) appendOrUpdatePlot checks if the given plot already exists in the plot_dict of the PlotterWidget and depending on
+ that, it either uses PlotterWidget.plot or PlotterWidget.replacePlot
+
+Strategy for integration of the demo (at first)
+
+Using methods that are already existing in SasView to feed all of the existing infrastructure
+ into the new TabbedPlotWidget
+Therefore there have to be places in the code where existing classes like for example DataExplorer, Plotter,
+ PlotterBase, GuiManager, FittingWidget are communicating with the new TabbedPlotWidget and tell them about the
+ requested plots and all the other information that is provided when fitting/simulating something.
+ These places in the code will feed the TabbedPlotWidget.
+
+Example: displayData is used as a method not to plot something directly but to manage what should be plotted and
+then give all that is needed to another method that plots. This can be used to not only plot in the original infra-
+structure, but also plot the same in the TabbedPlotWidget
+
+Therefore, some of the classes can interact with tabbedPlotWidget (which is a variable/object of GuiManager right now)
+and tell it to do something. The classes therefore interact via its parent with self.parent.tabbedPlotWidget or similar
+
+In order to use the existing infrastructure, it is important to understand the reason behind all the methods that are
+included into the plotting process and then understand how this can be used to feed all the needed information for
+a similar plotting behaviour in the TabbedPlotWidget.
+
+Overview: How to get to the TabbedFittingWidget from some classes that might need access:
+ from Dataexplorer: self.parent.tabbedPlotWidget
+ from Plotter: self.parent.parent.tabbedPlotWidget
+ from PlotterBase: self.parent.parent.tabbedPlotWidget
+ from GuiManager: self.tabbedPlotWidget
+
+
+Dictionaries for plot management:
+DataExplorerWindow.active_plots: all of the PlotterWidget instances are added in here to keep track of the windows?
+PlotterWidget.plot_dict: keep track of all the datasets that have been printed in this particular window, so that no
+ other window with the same dataset is displayed? (this is just a conjecture at the moment)
+
+Structure of the Subtabs(which extends QTabWidget):
+-> Tabs are DockContainers(QMainWindows)
+-> setting the widget of the DockContainers with the docking zone to CanvasWidgets(QWidgets)
+-> CanvasWidgets are filled with layouts that have only one widget at the moment: a ClickableCanvas(FigureCanvasQtAgg)
+-> ClickableCanvas has the figure and extends a FigureCanvasQtAgg that can act upon events on the canvas like clicking
+
+Problem/Challenge: The figure for the canvas and the layout for the CanvasWidget(which can include a matplotlib
+ navigation toolbar if this is needed) have to be there before creating all the objects. Then one can give the layout
+ and the figure into the creation and set them this way
+
+Problem/Challenge: Existing Axes objects from Matplotlib that come with all the other handy stuff (title, labels, ...)
+cannot be transferred to other figures very easily. Therefore, the created Axes from the Plotter cannot just be
+given back up the stream and then be integrated into the TabbedPlotWidget
+ Possible solution: When plotting something in the TabbedPlotWidget, just create an axes and feed it into the
+ plotting functionality of the Plotter afterwards. Then, all the steps are made on the right axes (again)
+Does this solution need to know, how many plots there are beforehand?
+
+
+Problems with the current solution of with plotting on both axes at the same time:
+1 In one of the Axes, Data and Fit are plotted twice
+2 Legends are breaking stuff with subplots because of the width of them
+3 The first plot seems to be empty always
+ -> This has maybe something to do with showing Fit+Data on another axes and thus this gets ignored?
+ -> For example when fitting a Data1d: for the merged fit and dataset plot, the for loop in plot dats is executed
+ twice instead of only once, because there is only one axes for it in there
+ this is also the case for the normal axes that exists in the qwidget, but it leads to too many axes being produced
+ by using len(plots) itself as an indicator for how many axes need to be generated in SubTabs
+
+ Solution could be: first iterating over the len(plots and check in there, how many plots are really needed
+4 2d plots do not work at all
+
+
+SubTabs need to be actually applied, right now, all plots are plotted in the first SubTab, but the SubTabs as a
+ QTabWidget are not actually used, everything is just stored in the first one.
+ This possibly also means, that parts of the code with the figure need to be refactored already, since
+ every Tab in the SubTab would need its own figure which could be complicated to be accessed through all the layers
+ from the QTabWidget SubTabs itself.
+appendOrUpdatePlot in DataExplorer needs refactoring to work with the TabbedPlotWidget
diff --git a/plotting_refactor/notes_and_sketches/Questions.txt b/plotting_refactor/notes_and_sketches/Questions.txt
new file mode 100644
index 0000000000..c6be66bf8b
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/Questions.txt
@@ -0,0 +1,29 @@
+What will the Window with all the tabs for plots be in terms of implementation?
+ Is the window permanent? Like a perspective for instance?
+ Is it belonging to the DataExplorerWindow?
+
+Same lifetime as SasView
+Showable and Hideable when opening other perspectives such as the invariant
+All the data should pass through the dataexplorer, plotwidget is owned by dataexplorer
+QStandardItemModels are used in the DataExplorer.py to keep the loaded data and the theory models
+QStandardItemModels can store data so that i can be directly accessed through qt (like a tree structure)
+Track the dictionary, as the dictionary changes, change the model as well
+
+Follow the model and the theory_model from DataExplorerWindow throughout
+other parts of the code and keep track of what is happening to it, how it is accessed
+and how it is edited
+
+
+
+How does the checking for an existing tab with locals() work? l.1206 sas.qtgui.MainWindow.DataExplorer
+locals as keeping track of what has been declared in this method call
+
+Is the data manager really redundant and are all the functionalities moved to GuiManager?
+Qt is able to stream stuff without carrying a pointer to the Datamanager everywhere it is needed
+
+
+Does it work that create_gui_data creates a new Data1D or Data2D element and parses the data into these
+items? then the new_plot items can be worked with and it can also have an id
+
+Communicate class in GuiUtils can collect signals from parts of the codes
+where signals are not processed in the same class but throughout different classes
\ No newline at end of file
diff --git a/plotting_refactor/notes_and_sketches/Report and Technical Documentation.docx b/plotting_refactor/notes_and_sketches/Report and Technical Documentation.docx
new file mode 100644
index 0000000000..ba43c3710c
Binary files /dev/null and b/plotting_refactor/notes_and_sketches/Report and Technical Documentation.docx differ
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_05_17.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_05_17.txt
new file mode 100644
index 0000000000..5e455c3215
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_05_17.txt
@@ -0,0 +1,25 @@
+How can trends be combined with the plotting functionality?
+Residuals displayed in another subplot on the bottom. - done
+unique ID by timestamp - done, implmented with current time and fitpageindex as a sum
+
+
+
+historical:
+Plot refactoring was kicked off at code camp isis
+concrete plans where there in jan 24 during the code camp
+
+what is planned in other branches at the time:
+generic data formats
+views in addition to data formats
+- data object lives in a view
+- in addition: measurement time or detector distance are added to the view as metadata that are there for every measurement of this set
+
+trends for packaging data:
+- multiple datasets on the same instrument for different temeperatures or pressures for example
+
+
+ideas for the demo:
+tree like structure for data and plots (almost done)
+Dataviewer owns plot widget for easier dataflow (done)
+combobox instead of new window for sending to subplot (done)
+
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_05_24.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_05_24.txt
new file mode 100644
index 0000000000..fbe250c2aa
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_05_24.txt
@@ -0,0 +1,34 @@
+plot refactoring meeting 24.05.2024
+
+arbitrary number of plots in one subplot window? e.g. 2x2?
+
+polydispersity subplot for single parameters
+
+for a trend: show subplots all in one subplot? like in a grid?
+
+dataselector tree structure -move data around
+sasview drag and drop mechanism for dataexplorer droppable data load widget
+SAS qtgui mainwindow
+
+
+mockup for subplots in one minor subplot window
+
+2dviewing for data and fit and residuals, 3 of them side by side?
+datatab - only data very big
+fittab - data and fit
+residualstab - all three
+
+dockable possibility for tabs and subtabs
+
+
+
+
+lin scale for the residuals
+
+
+
+2d plotting different 2d scaling for different detectors on the same instrument
+
+
+
+
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_05_31.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_05_31.txt
new file mode 100644
index 0000000000..00b61d42ed
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_05_31.txt
@@ -0,0 +1,21 @@
+2D mockup for next week
+
+
+highlight a plot by clicking and then it is enlarged and when you click another one it goes back
+ how can the plots be recognized
+
+manipulations for one plot so that it is only in that particular plot
+
+
+sasview beta is coming out soon
+
+inside the qtreewidget one can access the class of the original item that was added to the tree by
+ using data or something
+
+qstandarditems have children that keep the objects that are originally fed
+
+
+shared intensity bar for all plots
+
+
+push my project to git
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_06_04.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_06_04.txt
new file mode 100644
index 0000000000..7d37843b3a
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_06_04.txt
@@ -0,0 +1,3 @@
+lucas demo is very good with the modifiers
+
+batch slicing - showing a slicer right next to the 2d
\ No newline at end of file
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_06_14.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_06_14.txt
new file mode 100644
index 0000000000..b6e07a80b6
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_06_14.txt
@@ -0,0 +1,21 @@
+stream object without Qdatastream but directly using the object
+
+transfer towards pyside instead of pyqt6
+
+2D plotting
+ Generate mock-ups (done 5/31)
+ Create functional systems based on mock ups
+ 3 plotting groups
+ Group 1 - main plot group - Initially show fit with residuals and data as subplots (all with the same color scale)
+ Clicking on one of the subplots swaps it with the primary plot
+ Group 2 - multiple main plots with the same color scale bar
+ Group 3 - multiple main plots with different scale bars
+ Each plot/subplot could be moved/added to any other group and scale bars would automatically update
+Drag-drop plots
+ Drag any plot(s) into a plot subgroup
+ Visual feedback where the plot(s) would go (e.g. a template box would appear where the plot will go as the plot group is dragged)
+Polydispertiy plots and where they should go
+plan for next week:
+ - continue with d&d (with no datastream, if possible)
+ - start with 2D implementation based on the general mockup
+ - dockable tabs for fitting
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_06_21.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_06_21.txt
new file mode 100644
index 0000000000..cba8cd649b
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_06_21.txt
@@ -0,0 +1,16 @@
+lucas new student on july 22nd
+
+user feedback for dragging onto plot tree widget items (possible or not possible)
+
+
+start 2d intensity implementing
+for 2d clicking:
+matplotlib can interact with the mouse clicks as well
+
+
+make a draft pull request and open up a directory for my files
+
+add screenshots for the 2d plots
+
+
+general plotting container where anything can be modified in any fashion?
\ No newline at end of file
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_07_05.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_07_05.txt
new file mode 100644
index 0000000000..fbc478207d
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_07_05.txt
@@ -0,0 +1,13 @@
+layout has more than one item remaining and thats why i cannot shrink further
+
+talking about the trend objects
+
+Thread objects for creating the data (few layers of abstraction in between)
+sasmodels kernel is a good starting point
+what it uses and what it is used by
+
+combining both functionalities from tabbed click and other demo
+
+modifier problem
+
+
diff --git a/plotting_refactor/notes_and_sketches/plot_refactoring_07_12.txt b/plotting_refactor/notes_and_sketches/plot_refactoring_07_12.txt
new file mode 100644
index 0000000000..49cb285d45
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/plot_refactoring_07_12.txt
@@ -0,0 +1,16 @@
+lucas student starting soon
+
+data has convolution for resolutions and e.g. hankel transformation
+builtin for models to go from the real model to the data
+
+comparing 2 models together?
+bumps- parallelizing model calculation, jeff is going to raffle? code camp to work on this
+
+how is the data serialized?
+does an earlier fit and data object live on, if the fitpage is recalculated?
+how can changes to an object be kept in storage? stacks?
+
+json objects in the background?
+tree or order in which the stuff happened? list of changes is temporary?
+
+where does the save and load live? - on main sasview
\ No newline at end of file
diff --git a/plotting_refactor/notes_and_sketches/sasview_plotting_sktech.png b/plotting_refactor/notes_and_sketches/sasview_plotting_sktech.png
new file mode 100644
index 0000000000..215392bac7
Binary files /dev/null and b/plotting_refactor/notes_and_sketches/sasview_plotting_sktech.png differ
diff --git a/plotting_refactor/notes_and_sketches/thoughts.txt b/plotting_refactor/notes_and_sketches/thoughts.txt
new file mode 100644
index 0000000000..ed49ee3a9c
--- /dev/null
+++ b/plotting_refactor/notes_and_sketches/thoughts.txt
@@ -0,0 +1,94 @@
+demo integration into sasview:
+
+what is needed?
+DataIDs from DataViewer/DataCollector, where are these saved in SasView?
+What does the current displaying system look like?
+
+How is a fit linked with the underlying data?
+How are residuals linked?
+How is polydispersity linked?
+First layer of the model QStandardItemModel would be Data nodes
+ children of these nodes would be e.g. the polydispersity, fit, residuals, etc.
+
+
+Relation datasets/fitpages <--> existing plots?
+How is the current logic behind plotting something new and replotting an existing plot
+Checking if certain id already exists in the model/the dictionary for plotted objects
+
+plotting tools for 1d and 2d exist and can keep functioning that way?
+They just produce QWidgets that can be embedded inside the tabs afterwards
+GuiManager-filesWidget.plotData creates an instance of PlotterWidget, which is a QWidget and
+
+
+Where are the QWidgets from the Plotting generated? Where is explicitly show() stated?
+
+dataID generation is in sas.qtgui.MainWindow.DataManager on line 124
+The ID is a cocatenated string of the data object name
+(another GUI-only element that will move to sasdata) and
+1 plus the time stamp when SasView​ was loaded, not when the data file was loaded.
+The time stamp will be relative to the data load time,
+not the loader instance creation time when fully implemented into
+the sasdata object.
+
+
+What is a model?
+How can the plot for a model with a certain name already exist in the GuiUtils,
+if the plotRequested Signal has not been emitted in the first place?
+Why is _requestPlots applying plot.plot_role != DataRole.ROLE_DATA and only giving signals where this is not true/true?
+Where does self.communicate.plotRequestedSignal go? What does this signal invoke?
+ self.communicate is coming from the parent through initialize globals
+ in initializeGlobals, the self.communicate = self.parent.communicate
+ communicate comes from GuiUtils.Communicate()?
+ How is this initialized if i create a new fitpage by hitting new fitpage?
+ Where is the original GuiUtils coming from? -maybe from GuiManager?
+
+
+
+
+
+
+Tree of what happens if python run.py is executed:
+run.py
+ run.__main__
+ sas.cli.main
+ sas.qtgui.MainWindow.MainWindow.run_sasview()
+ QApplication is setup
+ QtReactor is installed -> sasview can then listen to client/server calls
+ and process them.
+ this is handled in sas.qtgui.Utilities.ReactorCore.install(),
+ where install is only defined at the very bottom and is dependent
+ on the system the user is using to start sasview (win32 or else?)
+ mainwindow is initialized with mainwindow = MainSasViewWindow():
+ self.guimanager = GuiManager(self) (it says main sassview window functionality itself)
+ ALOT of stuff is done and a lot of stuff is imported
+ self._datamanger = DataManager()
+ self.addWidgets()
+ What signal callbacks? l.106
+ addCallbacks() l.670
+ self.communicate = GuiUtils.Communicate()
+ a big bunch of signals that are connected with methods
+ e.g. self.communicate.plotRequestedSignal.connect(self.showPlot)
+ showPlot(self, plot, id)
+ self.filesWidget.displayData(plot, id)
+ What Categories?
+ All categories are listed in (user folder)
+ ~/sasview/categories.json and are built there
+ by the sas.qtgui.utilities.categoryinstaller.categoryinstaller
+ And all the existing models come from
+ sas.sascalc.fit.models.ModelManager.cat_model_list()
+ this gives back a listed variant of all models
+ that are existing in the standard library. but it only goes through
+ sasmodels.sasmodels.sasview_model.load_standard_models() (and before it goes through sasview.src.sas.sascalc.fit.models.ModelManagerBase and ModelManagers
+ what action triggers? l116
+
+
+
+Where does the data come from?
+DataManager manages all the data that is loaded from a certain path and the ones
+from creation at loading time
+DataManager.create_gui_data can receive data from loader and create a data to use for guiframe
+This will depend on Data2D and Data1D objects from PlotterData
+
+
+
+
\ No newline at end of file