Repository: AaltoPML/VAI-Lab (language: python)

Issue:
Css Bug fixing
# Purpose

Fix different bugs existing in the code and enhance the GUI experience.

# XML loading

Fixed requirement to load pluginCanvas when loading an XML file regardless of its stored information. It now checks if every module has a plugin and, if so, it allows to run the pipeline. Nevertheless, you can still open the `pluginCanvas` and modify the file at will.

# `pluginCanvas`

Fixed error when going back to main without loading a pipeline.  Added saving setting options in the XML file.

## Plugin Settings Window

This window is modified to work on window resize and dynamically add entries based on the settings. It now reads the required and optional settings from the class descriptor and first displays the required options and then the optional ones. Besides, now you can click enter when on an entry to move to the next available one up to the last one, which leads to the `Finish` button. 

At the same time, when an entry is empty, it shows its required information, e.g. `list` or `str`.

# `aidCanvas` and `manualInput`

Both plugins have been adapted to having several frames per window. This, in turn, corresponds to the same structure the other windows follows and allows resizing the window and dynamically resizing the objects within. 


Discussion / hints:

Fix patch:
diff --git a/aidesign/Core/AI_design_core.py b/aidesign/Core/AI_design_core.py
index c85a70c1..b73e7bfe 100644
--- a/aidesign/Core/AI_design_core.py
+++ b/aidesign/Core/AI_design_core.py
@@ -1,7 +1,6 @@
-# from importlib import import_module
 from aidesign.utils.import_helper import import_module
-from .. import Settings
-from .. import GUI
+from aidesign.Settings.Settings_core import Settings
+from aidesign.GUI.GUI_core import GUI
 
 
 class Core(Settings):
@@ -10,7 +9,7 @@ def __init__(self) -> None:
         self.loop_level = 0
 
     def launch(self):
-        gui_app = GUI.GUI()
+        gui_app = GUI()
         gui_app.set_plugin_name('main')
         gui_output = gui_app.launch()
         if not gui_app.closed:
@@ -64,6 +63,7 @@ def _execute_exit_point(self, specs):
     def _parse_condition(self, condition):
         try:
             condition = int(condition)
+            
             if isinstance(condition, int):
                 return range(0, condition)
         except:
diff --git a/aidesign/Core/__init__.py b/aidesign/Core/__init__.py
index 169fa711..3750ece6 100644
--- a/aidesign/Core/__init__.py
+++ b/aidesign/Core/__init__.py
@@ -5,4 +5,4 @@
 Let's look for a more automatic way of doing this when we have more modules
 """
 from .AI_design_core import Core
-from .. import *
\ No newline at end of file
+# from .. import *
\ No newline at end of file
diff --git a/aidesign/DataProcessing/Modelling_core.py b/aidesign/DataProcessing/Modelling_core.py
new file mode 100644
index 00000000..97a81d24
--- /dev/null
+++ b/aidesign/DataProcessing/Modelling_core.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+class Modelling(object):
+    def __init__(self):
+        self.node_name = None
+        self.plugin_name = None
+        self.output_data = None
+
+    def set_input_data(self, data):
+        self.input_data = data
+
+    def set_target_data(self, data):
+        self.target_data = data
+
+    def _load_plugin(self):
+        pass
+
+    def set_plugin_name(self, plugin_name):
+        self.plugin_name = plugin_name
+        self._load_plugin()
+
+    def set_options(self, options):
+        self.options = options
+
+    def solve(self):
+        # send the input data to the modelling plugin and assign it to an output property
+        pass
+
+    def get_result(self):
+        return self.output_data
diff --git a/aidesign/DataProcessing/__init__.py b/aidesign/DataProcessing/__init__.py
new file mode 100644
index 00000000..99e78a66
--- /dev/null
+++ b/aidesign/DataProcessing/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+"""Import all submodules inside this folder and expose them with their respective names
+
+Let's look for a more automatic way of doing this when we have more modules
+"""
+from .Modelling_core import Modelling
diff --git a/aidesign/DecisionMaking/Modelling_core.py b/aidesign/DecisionMaking/Modelling_core.py
new file mode 100644
index 00000000..97a81d24
--- /dev/null
+++ b/aidesign/DecisionMaking/Modelling_core.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+class Modelling(object):
+    def __init__(self):
+        self.node_name = None
+        self.plugin_name = None
+        self.output_data = None
+
+    def set_input_data(self, data):
+        self.input_data = data
+
+    def set_target_data(self, data):
+        self.target_data = data
+
+    def _load_plugin(self):
+        pass
+
+    def set_plugin_name(self, plugin_name):
+        self.plugin_name = plugin_name
+        self._load_plugin()
+
+    def set_options(self, options):
+        self.options = options
+
+    def solve(self):
+        # send the input data to the modelling plugin and assign it to an output property
+        pass
+
+    def get_result(self):
+        return self.output_data
diff --git a/aidesign/DecisionMaking/__init__.py b/aidesign/DecisionMaking/__init__.py
new file mode 100644
index 00000000..99e78a66
--- /dev/null
+++ b/aidesign/DecisionMaking/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+"""Import all submodules inside this folder and expose them with their respective names
+
+Let's look for a more automatic way of doing this when we have more modules
+"""
+from .Modelling_core import Modelling
diff --git a/aidesign/GUI/GUI_core.py b/aidesign/GUI/GUI_core.py
index 0322bf53..947c01cd 100644
--- a/aidesign/GUI/GUI_core.py
+++ b/aidesign/GUI/GUI_core.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 import tkinter as tk
-from aidesign.utils.import_helper import import_plugin
+from tkinter.font import Font
+from aidesign.utils.import_helper import import_plugin_absolute
+from aidesign.utils.plugin_helpers import PluginSpecs
 
 class GUI(tk.Tk):
     """
@@ -12,10 +14,10 @@ class GUI(tk.Tk):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        self.title_font = tk.font.Font(family='Helvetica',
+        self.title_font = Font(family='Helvetica',
                                       size=14,
                                       weight="bold")
-        self.pages_font = tk.font.Font(family='Helvetica',
+        self.pages_font = Font(family='Helvetica',
                                       size=12)
 
         self._desired_ui_types = []
@@ -23,67 +25,37 @@ def __init__(self, *args, **kwargs):
         self._module_config = None
         self.closed = False
         self.output = {}
+        self._plugin_specs = PluginSpecs()
 
-        self._available_ui_types = {
-            "MainPage": {
-                "name": "main",
-                "layer_priority": 1,
-                "required_children": ['aidCanvas']
-            },
-            "aidCanvas": {
-                "name": "aidCanvas",
-                "layer_priority": 1,
-                "required_children": []
-            },
-            "ManualInput": {
-                "name": "manual",
-                "layer_priority": 2,
-                "required_children": None,
-            },
-            "CanvasInput": {
-                "name": "canvas",
-                "layer_priority": 2,
-                "required_children": None,
-            }
-        }
-
-    def _compare_layer_priority(self, ui_name):
+    def _compare_layer_priority(self, ui_specs):
         """Check if a new module should have higher layer priority than the existing one
 
         :param ui_name: name of the UI method being compared
         :type ui_name: str
         """
         if self._top_ui_layer == None:
-            self._top_ui_layer = ui_name
+            self._top_ui_layer = ui_specs
         else:
-            current_top_layer = self._available_ui_types[self._top_ui_layer]["layer_priority"]
-            candidate_layer = self._available_ui_types[ui_name]["layer_priority"]
+            current_top_layer = self._top_ui_layer["_PLUGIN_MODULE_OPTIONS"]["layer_priority"]
+            candidate_layer = ui_specs["_PLUGIN_MODULE_OPTIONS"]["layer_priority"]
             self._top_ui_layer = candidate_layer \
                 if candidate_layer < current_top_layer \
                 else self._top_ui_layer
 
-    def _add_UI_type_to_frames(self, ui_name):
+    def _add_UI_type_to_frames(self, ui_specs):
         """Add user defined UI method to list of frames to be loaded
 
         :param ui_name: name of the UI method being loaded
         :type ui_name: str 
         """
-        try:
-            plugin = import_plugin(globals(),ui_name)
-        except:
-            from sys import exit
-            print(
-                "Error: User Interface \"{0}\" not recognised. \
-                \nAvailable methods are: \
-                \n  - {1}"\
-                .format(ui_name, ",\n  - ".join(
-                    [i["name"]for i in self._available_ui_types.values()])))
-            exit(1)
+        plugin = import_plugin_absolute(globals(),
+                                        ui_specs["_PLUGIN_PACKAGE"],
+                                        ui_specs["_PLUGIN_CLASS_NAME"])
         self._desired_ui_types.append(plugin)
-        self._compare_layer_priority(ui_name)
-        if self._available_ui_types[ui_name]["required_children"] != None:
-            for children in self._available_ui_types[ui_name]["required_children"]:
-                self._add_UI_type_to_frames(children)
+        self._compare_layer_priority(ui_specs)
+        if ui_specs["_PLUGIN_MODULE_OPTIONS"]["required_children"] != None:
+            for children in ui_specs["_PLUGIN_MODULE_OPTIONS"]["required_children"]:
+                self.set_plugin_name(children)
 
     def set_plugin_name(self, ui_type: list):
         """"Given user input, create a list of classes of the corresponding User Interface Type 
@@ -96,9 +68,22 @@ def set_plugin_name(self, ui_type: list):
             else [ui_type]
 
         for ui in ui_type:
-            ui_name = ''.join(kn for kn in self._available_ui_types.keys()
-                              if ui.lower() == self._available_ui_types[kn]["name"])
-            self._add_UI_type_to_frames(ui_name)
+            ui_specs = self._plugin_specs.find_from_readable_name(ui)
+            try:
+                self._add_UI_type_to_frames(ui_specs)
+            except ModuleNotFoundError as ex:
+                from sys import exit
+                print(ex.msg)
+                exit(1)
+            except:
+                from sys import exit
+                print(
+                    "Error: User Interface \"{0}\" not recognised.\
+                    \n Available methods are:\
+                    \n   - {1}"\
+                    .format(ui, ",\n   - ".join(
+                        [i for i in self._plugin_specs.available_plugin_names])))
+                exit(1)
 
     def _append_to_output(self, key:str, value:any):
         self.output[key] = value
@@ -113,6 +98,8 @@ def set_options(self, module_config: dict):
 
     def _show_frame(self, page_name):
         '''Show a frame for the given page name'''
+        if isinstance(page_name,dict):
+            page_name = page_name["_PLUGIN_CLASS_NAME"]
         frame = self.frames[page_name]
         frame.tkraise()
 
diff --git a/aidesign/GUI/__init__.py b/aidesign/GUI/__init__.py
index c05d8468..47883088 100644
--- a/aidesign/GUI/__init__.py
+++ b/aidesign/GUI/__init__.py
@@ -4,7 +4,7 @@
 
 Let's look for a more automatic way of doing this when we have more modules
 """
-from .User_Feedback_template import UI
-from .GUI_core import GUI
+# from .User_Feedback_template import UI
+# from .GUI_core import GUI
 
-from .. import *
\ No newline at end of file
+# from .. import *
\ No newline at end of file
diff --git a/aidesign/GUI/plugins/CanvasInput.py b/aidesign/GUI/plugins/CanvasInput.py
deleted file mode 100644
index 3d052d3c..00000000
--- a/aidesign/GUI/plugins/CanvasInput.py
+++ /dev/null
@@ -1,628 +0,0 @@
-from . import UI
-import tkinter as tk
-import os
-
-from tkinter import messagebox, ttk
-
-from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
-import numpy as np
-import pandas as pd
-import math
-
-class CanvasInput(tk.Frame,UI):
-    def __init__(self, parent, controller, config:dict):
-        self.parent = parent
-        super().__init__(self.parent, bg = self.parent['bg'])
-        self.controller = controller
-        
-        self.controller.title('Canvas Input')
-        self.notebook = ttk.Notebook(self)
-        self.notebook.grid(row=0, column=0, columnspan=7, 
-                           rowspan = 12, pady = 15)
-        self.frame = []
-        self.tree = []
-        self.canvas = []
-        self.draw = []
-        self.state = []
-        self.type = []
-        self.clock = []
-        self._parse_config(config)
-        self.save_path = ''
-        self.saved = True
-    
-    def _parse_config(self, config):
-        self.config = config
-        self.class_list(self.config["plugin"]["options"]["class_list"])
-
-    def class_list(self):
-        """Getter for required _class_list variable
-        
-        :return: list of class labels
-        :rtype: list of strings
-        """
-        return self._class_list
-
-    def class_list(self, value):
-        """Setter for required _class_list variable
-        
-        :param value: labels for state-action pair headers
-        :type value: list of strings
-        """
-        self._class_list = value
-        self.out_data = {}
-        for ii in np.arange(len(self._class_list)):
-            self.out_data[ii] = {}
-            for key in self._class_list[ii]:
-                self.out_data[ii][key] = []
-
-        for ii in np.arange(len(self._class_list)):
-            self.frame.append(tk.Frame(self, bg = self.parent['bg']))
-            self.frame[-1].grid(row=0, column=0, sticky="nsew", pady = 15)
-            if self._class_list[ii][0].split('_')[1] == 'a':
-                self.type.append('Rotating')
-                self.clock.append(tk.StringVar())
-                self.clock[ii].set('clock')
-            else:
-                self.type.append('Sliding')
-                self.clock.append(-1)
-            self.notebook.add(
-                self.frame[-1], 
-                text = 'Object '+ str(ii) + ' - ' + self.type[-1])
-                
-            
-            # Create a canvas widget
-            self.width, self.height = 600, 600
-            self.canvas.append(tk.Canvas(
-                self.frame[-1], width=self.width, 
-                height=self.height, background="white"))
-            self.canvas[-1].grid(row=0, column=0, columnspan=4, rowspan = 2)
-            self.canvas[-1].bind('<Button-1>', self.draw_dot)
-            self.canvas[-1].bind("<B1-Motion>", self.on_drag)
-            # self.canvas[-1].bind('<Motion>', self.motion)
-            
-            # Booleans to identify what the user wants to do
-            self.draw.append(tk.StringVar())
-            self.draw[ii].set('draw')
-            self.state.append(tk.StringVar())
-            self.state[ii].set('state')    
-            # Buttons under the canvas
-            self.button_draw = tk.Radiobutton(
-                self.frame[-1], text = 'Draw', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.draw[ii], 
-                selectcolor = 'black', value = 'draw').grid(column = 4, row = 3)
-            self.button_drag = tk.Radiobutton(
-                self.frame[-1], text = 'Move', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.draw[ii], 
-                selectcolor = 'black', value = 'drag').grid(column = 5, row = 3)
-            self.clock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Clockwise', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.clock[ii], 
-                selectcolor = 'black', value = 'clock').grid(column = 6, row = 3)            
-            self.countclock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Counterclockwise', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.clock[ii], 
-                selectcolor = 'black', value = 'countclock').grid(column = 7, row = 3)
-            # self.button_edit = tk.Radiobutton(
-            #     self.frame[-1], text = 'Edit', fg = 'white', bg = self.parent['bg'],
-            #     height = 3, width = 20, var = self.draw[ii], 
-            #     selectcolor = 'black', value = 'edit').grid(column = 6,row = 3)
-            self.button_save = tk.Button(
-                self.frame[-1], text = 'Save', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, 
-                command = self.save_file).grid(column = 1, row = 3)
-            self.button_upload = tk.Button(
-                self.frame[-1], text = 'Upload coordinates', fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20, 
-                command = self.upload_sa).grid(column = 0, row = 3)
-            self.button_reset = tk.Button(
-                self.frame[-1], text = 'Reset', fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20, 
-                command = self.reset).grid(column = 2, row = 3)
-            button_main = tk.Button(
-                self.frame[-1], text="Done", fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20,
-                command = self.check_quit).grid(column = 3, row = 3)
-            style = ttk.Style()
-            style.configure(
-                "Treeview", background = 'white', foreground = 'white', 
-                rowheight = 25, fieldbackground = 'white', 
-                font = self.controller.pages_font)
-            style.configure("Treeview.Heading", 
-                            font = self.controller.pages_font)
-            style.map('Treeview', background = [('selected', 'grey')])
-            
-
-            tree_frame = tk.Frame(self.frame[-1])
-            tree_frame.grid(row = 0, column = 4, columnspan = 3, rowspan = 10)
-            
-            tree_scrollx = tk.Scrollbar(tree_frame, orient = 'horizontal')
-            tree_scrollx.pack(side = tk.BOTTOM, fill = tk.X)
-            tree_scrolly = tk.Scrollbar(tree_frame)
-            tree_scrolly.pack(side = tk.RIGHT, fill = tk.Y)
-            
-            self.tree.append(ttk.Treeview(
-                tree_frame, 
-                yscrollcommand = tree_scrolly.set, 
-                xscrollcommand = tree_scrollx.set))
-            self.tree[-1].pack()
-            
-            tree_scrollx.config(command = self.tree[-1].xview)
-            tree_scrolly.config(command = self.tree[-1].yview)
-            
-            if self.type[-1] == 'Rotating':
-                self.x_ini, self.y_ini = int(self.width/2), int(self.height/2)
-                self.r = int(np.min([self.x_ini, self.y_ini])/1.5)
-                self.create_circle(self.x_ini, self.y_ini, 4, 
-                                    fill="#DDD", outline="", width=4)
-                self.create_circle(self.x_ini, self.y_ini, self.r, 
-                                    fill="", outline="#DDD", width=4)
-                
-            self.tree[-1]['columns'] = self._class_list[ii]
-            
-            # Format columns
-            self.tree[-1].column("#0", width = 50)
-            for n, cl in enumerate(self._class_list[ii]):
-                self.tree[-1].column(
-                    cl, width = int(
-                        self.controller.pages_font.measure(str(cl)))+20, 
-                    minwidth = 50, anchor = tk.CENTER)
-                    
-            # Headings
-            self.tree[-1].heading("#0", text = "Sample", anchor = tk.CENTER)
-            for cl in self._class_list[ii]:
-                self.tree[-1].heading(cl, text = cl, anchor = tk.CENTER)
-            
-            # for nn in np.arange(N):
-            #     self.tree[-1].insert(
-            #                 parent = '', index = 'end', iid = (nn+1)*1000, text = 'Trial '+str(nn), 
-            #                 values = tuple())
-            
-            self.tree[-1].tag_configure('odd', foreground = 'black', 
-                                        background='#E8E8E8')
-            self.tree[-1].tag_configure('even', foreground = 'black', 
-                                        background='#DFDFDF')
-        
-            # Define double-click on row action
-            self.tree[-1].bind("<Double-1>", self.OnDoubleClick)
-
-    def draw_dot(self, event):
-        
-        ii = self.notebook.index(self.notebook.select())
-        if self.draw[ii].get() == 'drag':
-            self.selected = self.canvas[ii].find_overlapping(
-                event.x-10, event.y-10, event.x+10, event.y+10)
-            if self.selected:
-                if len(self.selected) > 2:
-                    self.canvas[ii].selected = self.selected[-2]
-                else:
-                    self.canvas[ii].selected = self.selected[-1]
-                self.canvas[ii].startxy = (event.x, event.y)
-            else:
-                self.canvas[ii].selected = None
-
-        if self.draw[ii].get()  == 'draw':
-            # Draw an oval in the given coordinates
-            self.saved = False
-            # Angle of the selected point with respect to the circle center
-            if self.type[ii] == 'Rotating':
-                alpha, x_o, y_o = self.angle_calc(event.x, event.y, 
-                                                  circxy=True)
-                if self.state[ii].get()  == 'state': # Write state coordinates
-                    self.canvas[ii].create_oval(
-                        x_o-3, y_o-3, x_o+3, y_o+3, fill="black", width=0, 
-                        tags=("state"+str(ii)+'-'
-                              + str(len(self.out_data[ii]['State_a']))))
-                    self.out_data[ii]['State_a'].append((
-                        -np.rad2deg(alpha)+360)%360)
-                    self.state[ii].set('action')
-                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
-                        n = int(self.tree[ii].selection()[0]) + 1
-                        if n%2 == 0:
-                            tag = ('even',)
-                        else:
-                            tag = ('odd',)
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = n, text = n+1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[n,:].astype(int)), 
-                            tags = tag)
-                        self.tree[ii].selection_set(str(n))
-                    else:
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = 0, text = 1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[0,:].astype(int)), 
-                                tags = ('even',))
-                        self.tree[ii].selection_set(str(0))
-                    
-                elif self.state[ii].get()  == 'action':
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['State_a'][-1]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['State_a'][-1] - 360
-                        end = 360 - np.rad2deg(alpha)
-                    self.create_circle_arc(
-                        self.x_ini, self.y_ini, self.r, fill = "", 
-                        outline = "red", start = start, end = end, width=2, 
-                        style = tk.ARC, tags = ("action"+str(ii)+'-' + str(len(
-                            self.out_data[ii]['Action_a']))))              
-                    self.out_data[ii]['Action_a'].append((
-                        -np.rad2deg(alpha)+360)%360)
-                    self.state[ii].set('state')
-                    n = int(self.tree[ii].selection()[0])
-                    self.tree[ii].item(
-                        self.tree[ii].get_children()[n], text = n+1, 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])[n,:].astype(int)))
-            else:
-                if self.state[ii].get()  == 'state': # Write state coordinates
-                    self.canvas[ii].create_oval(
-                        event.x-3, event.y-3, event.x+3, event.y+3, 
-                        fill="black", width=0, 
-                        tags=("state"+str(ii)+'-'+ str(len(
-                            self.out_data[ii]['State_x']))))
-                    self.out_data[ii]['State_x'].append(event.x)
-                    self.out_data[ii]['State_y'].append(event.y)
-                    self.state[ii].set('action')
-                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
-                        n = int(self.tree[ii].selection()[0]) + 1
-                        if n%2 == 0:
-                            tag = ('even',)
-                        else:
-                            tag = ('odd',)
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = n, text = n+1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[n,:].astype(int)), 
-                            tags = tag)
-                        self.tree[ii].selection_set(str(n))
-                    else:
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = 0, text = 1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[0,:].astype(int)), 
-                                tags = ('even',))
-                        self.tree[ii].selection_set(str(0))
-                    
-                elif self.state[ii].get()  == 'action':
-                    self.canvas[ii].create_line(
-                        self.out_data[ii]['State_x'][-1], 
-                        self.out_data[ii]['State_y'][-1], event.x, event.y, 
-                        fill="red", arrow=tk.LAST, 
-                        tags=("action"+str(ii)+'-' + str(len(
-                            self.out_data[ii]['Action_x']))))
-                    self.out_data[ii]['Action_x'].append(event.x)
-                    self.out_data[ii]['Action_y'].append(event.y)
-                    self.state[ii].set('state')
-                    n = int(self.tree[ii].selection()[0])
-                    self.tree[ii].item(
-                        self.tree[ii].get_children()[n], text = n+1, 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])[n,:].astype(int)))
-        
-    def on_drag(self, event):
-        
-        ii = self.notebook.index(self.notebook.select())
-        if self.draw[ii].get()  == 'drag' and self.canvas[ii].selected:
-            # move the selected item
-            n = int(self.canvas[ii].gettags("current")[0].split('-')[1])
-            
-            if self.type[ii] == 'Rotating':
-                alpha, dx, dy = self.angle_calc(event.x, event.y, circxy=True)
-                _, dx_pr, dy_pr = self.angle_calc(
-                    self.canvas[ii].startxy[0], self.canvas[ii].startxy[1], 
-                    circxy=True)
-                xa = self.r * np.cos(
-                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.x_ini
-                ya = self.r * np.sin(
-                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.y_ini
-                if self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
-                    self.canvas[ii].move(
-                        self.canvas[ii].selected, dx-dx_pr, 
-                        dy-dy_pr)
-                    self.out_data[ii]['State_a'][n] = (360-np.rad2deg(alpha))%360
-
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['Action_a'][n]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['Action_a'][n] - 360
-                        end = 360 - np.rad2deg(alpha)
-
-                    self.canvas[ii].itemconfigure(
-                        "action"+str(ii)+'-'+str(n), start = start, 
-                        extent = end - start)
-                    
-                elif self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
-                    
-                    self.out_data[ii]['Action_a'][n] = (360-np.rad2deg(alpha))%360
-
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['State_a'][n]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['State_a'][n] - 360
-                        end = 360 - np.rad2deg(alpha)
-                    
-                    self.canvas[ii].itemconfigure(
-                        "action"+str(ii)+'-'+str(n), start = start, 
-                        extent = end - start)
-
-            else:
-                # calculate distance moved from last position
-                dx = event.x - self.canvas[ii].startxy[0]
-                dy = event.y - self.canvas[ii].startxy[1]
-                if self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
-                    self.canvas[ii].move(self.canvas[ii].selected, dx, dy)
-                    self.canvas[ii].coords(
-                        "action"+str(ii)+'-'+str(n), 
-                        (event.x, event.y, self.out_data[ii]['Action_x'][n], 
-                        self.out_data[ii]['Action_y'][n]))
-                    self.out_data[ii]['State_x'][n] = event.x
-                    self.out_data[ii]['State_y'][n] = event.y  
-                elif self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
-                    self.canvas[ii].coords(
-                        self.canvas[ii].gettags("current")[0], 
-                        (self.out_data[ii]['State_x'][n], 
-                         self.out_data[ii]['State_y'][n], event.x, event.y))
-                    self.out_data[ii]['Action_x'][n] = event.x
-                    self.out_data[ii]['Action_y'][n] = event.y
-            # update last position
-            self.canvas[ii].startxy = (event.x, event.y)                
-            self.tree[ii].item(self.tree[ii].get_children()[n], text = n+1, 
-                           values = tuple(self.dict2mat(
-                               self.out_data[ii])[n,:].astype(int)))
-            self.tree[ii].selection_set(str(n))
-
-    def checkered(self, line_distance):
-        
-        ii = self.notebook.index(self.notebook.select()) 
-        # vertical lines at an interval of "line_distance" pixel
-        for x in range(line_distance, self.width, line_distance):
-            self.canvas[ii].create_line(x, 0, x, self.height, fill="#476042")
-        # horizontal lines at an interval of "line_distance" pixel
-        for y in range(line_distance, self.height, line_distance):
-            self.canvas[ii].create_line(0, y, self.width, y, fill="#476042")   
-            
-    def check_quit(self):
-        if not self.saved:
-            response = messagebox.askokcancel(
-                "Exit?", 
-                "Do you want to leave the program without saving?")
-            if response:
-                self.controller.destroy()
-        else:
-            response = messagebox.askokcancel(
-                "Exit?",
-                "Are you sure you are finished?")
-            self.controller.destroy()
-            
-    def save_file_as(self):
-        self.save_path = asksaveasfile(mode='w')
-        self.save_file()
-        
-    def save_file(self):
-        
-        if self.save_path == '':
-            self.save_path = asksaveasfile(defaultextension = '.txt', filetypes = 
-                                      [('Text file', '.txt'), 
-                                       ('CSV file', '.csv'), 
-                                       ('All Files', '*.*')])
-        if self.save_path is not None: # asksaveasfile return `None` if dialog closed with "cancel".
-            # Transform input for output file    
-            data = {}
-            samples = np.max([len(self.out_data[element][
-                list(self.out_data[element].keys())[0]]) 
-                for element in self.out_data])
-            for ii in self.out_data.keys():
-                for c in self.out_data[ii].keys():
-                    a, b = c.split('_')
-                    aux = self.out_data[ii][c]
-                    aux.extend([-1]*(samples - len(self.out_data[ii][c])))
-                    data[a+str(ii)+'_'+b] = aux
-                    
-            filedata = pd.DataFrame(data, columns = data.keys()).to_string()
-            self.save_path.seek(0) # Move to the first row to overwrite it
-            self.save_path.write(filedata)
-            self.save_path.flush() # Save without closing
-            # typically the above line would do. however this is used to ensure that the file is written
-            os.fsync(self.save_path.fileno())
-            self.saved = True
-         
-    def on_return(self, event):
-        ii = self.notebook.index(self.notebook.select())
-        val = self.tree[ii].item(self.treerow)['values']
-        val = [float(i) for i in val]
-        val[int(self.treecol[1:])-1] = float(self.entry.get())
-        self.tree[ii].item(self.treerow, values = val)
-        self.entry.destroy()        
-        self.saved = False
-        
-        if self.type[ii] == 'Rotating':
-            dx, dy = self.coord_calc(np.deg2rad(360 - val[0]))
-            dx_pr, dy_pr = self.coord_calc(np.deg2rad(360 - self.out_data[ii]['State_a'][self.treerow]))            
-            self.out_data[ii]['State_a'][self.treerow] = float(val[0])
-            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx-dx_pr, 
-                dy-dy_pr)
-            
-            self.out_data[ii]['Action_a'][self.treerow] = float(val[1])
-            if self.clock[ii].get()  == 'clock':
-                start = self.out_data[ii]['State_a'][self.treerow]
-                end = self.out_data[ii]['Action_a'][self.treerow]
-            else:
-                start = self.out_data[ii]['State_a'][self.treerow] - 360
-                end = self.out_data[ii]['Action_a'][self.treerow]
-            
-            self.canvas[ii].itemconfigure(
-                "action"+str(ii)+'-'+str(self.treerow), start = start, 
-                extent = end - start)
-        else:
-            # calculate distance moved from last position
-            dx_s = val[0] - self.out_data[ii]['State_x'][self.treerow]
-            dy_s = val[1] - self.out_data[ii]['State_y'][self.treerow]
-
-            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx_s, dy_s)
-
-            self.out_data[ii]['State_x'][self.treerow] = val[0]
-            self.out_data[ii]['State_y'][self.treerow] = val[1]
-
-            self.canvas[ii].coords(
-                "action"+str(ii)+'-'+str(self.treerow), 
-                (self.out_data[ii]['State_x'][self.treerow], 
-                  self.out_data[ii]['State_y'][self.treerow], val[2], val[3]))
-            self.out_data[ii]['Action_x'][self.treerow] = val[2]
-            self.out_data[ii]['Action_y'][self.treerow] = val[3]
-            
-    def OnDoubleClick(self, event):
-        
-        """ Executed when a row is double clicked.
-        Opens an entry box to edit a cell and updates the canvas and the 
-        stored data. """
-        
-        ii = self.notebook.index(self.notebook.select())
-        self.treerow = int(self.tree[ii].identify_row(event.y))
-        self.treecol = self.tree[ii].identify_column(event.x)
-        
-        # get column position info
-        x, y, width, height = self.tree[ii].bbox(self.treerow, self.treecol)
-    
-        # y-axis offset
-        pady = height // 2
-        # pady = 0
-        
-        if hasattr(self, 'entry'):
-            self.entry.destroy()
-            
-        self.entry = tk.Entry(self.tree[ii], justify='center') 
-        
-        if int(self.treecol[1:]) > 0:
-            self.entry.insert(
-                0, self.tree[ii].item(self.treerow)['values'][int(str(self.treecol[1:]))-1])
-            # self.entry['selectbackground'] = '#123456'
-            self.entry['exportselection'] = False
-    
-            self.entry.focus_force()
-            self.entry.bind("<Return>", self.on_return)
-            self.entry.bind("<Escape>", lambda *ignore: self.entry.destroy())
-            
-            self.entry.place(x = x, 
-                             y = y + pady, 
-                             anchor = tk.W, width = width)
-
-    def upload_sa(self):
-        
-        ii = self.notebook.index(self.notebook.select())
-        filename = askopenfilename(initialdir = os.getcwd(), 
-                                   title = 'Select a file', 
-                                   defaultextension = '.txt', 
-                                   filetypes = [('Text file', '.txt'), 
-                                                ('CSS file', '.css'), 
-                                                ('All Files', '*.*')])
-        if filename is not None:
-            data = open(filename,'r')
-            read = False
-            self.draw[ii].set('drag')
-            for n, point in enumerate(data):
-                if read: # Not elegant at all, just to omit the header.
-                    i, sx, sy, ax, ay = point.split()
-                    # Draw an oval in the given coordinates
-                    self.canvas[ii].create_oval(
-                        float(sx)-3, float(sy)-3, float(sx)+3, float(sy)+3, 
-                        fill="black", width=0, 
-                        tags=("state-" + str(len(
-                            self.out_data[ii]['State_x']))))
-                    self.canvas[ii].create_line(
-                        float(sx), float(sy), float(ax), float(ay), 
-                        fill="red", arrow=tk.LAST, 
-                        tags=("action"+str(ii)+"-"+ str(len(
-                            self.out_data[ii]['Action_x']))))
-                    self.out_data[ii]['State_x'].append(sx)
-                    self.out_data[ii]['State_y'].append(sy)
-                    self.out_data[ii]['Action_x'].append(ax)
-                    self.out_data[ii]['Action_y'].append(ay)
-                    self.tree.insert(
-                        parent = '', index = 'end', 
-                        iid = len(self.out_data[ii]['Action_x'])-1, 
-                        text = len(self.out_data[ii]['Action_x']), 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])
-                            [len(self.out_data[ii]
-                                 ['Action_x'])-1,:].astype(int)))
-                    self.tree.selection_set(str(len(
-                        self.out_data[ii]['Action_x'])-1))
-                else:
-                    read = True
-    
-    def reset(self):
-        
-        ii = self.notebook.index(self.notebook.select())
-        msg = messagebox.askyesnocancel(
-            'Info', 'Are you sure you want to reset the canvas?')
-        if msg:
-            self.canvas[ii].delete(tk.ALL) # Reset canvas
-            self.state[ii].set('state')
-            self.out_data[ii] = {}
-            for key in self._class_list[ii]:
-                self.out_data[ii][key] = []
-                    
-            for record in self.tree[ii].get_children(): #Reset treeview
-                self.tree[ii].delete(record)
-            if self.type[ii] == 'Rotating': 
-                self.create_circle(self.x_ini, self.y_ini, 4, 
-                                   fill="#DDD", outline="", width=4)
-                self.create_circle(self.x_ini, self.y_ini, self.r, 
-                                   fill="", outline="#DDD", width=4)
-    
-    def dict2mat(self, X):
-        
-        max_size = 0
-        for key in X:
-            if len(X[key]) > max_size:
-                max_size = len(X[key])
-
-        result = -np.ones((max_size, len(X)))
-        for k, key in enumerate(X):
-            result[:len(X[key]), k]  = X[key]
-            
-        return result
-    
-    def angle_calc(self, x, y, circxy = False):
-        if (x == self.x_ini) & (y < self.y_ini):
-            alpha = math.pi/2
-        elif (x == self.x_ini) & (y > self.y_ini):
-            alpha = 3*math.pi/2
-        else:
-            alpha = np.arctan((y-self.y_ini)/(x-self.x_ini))
-            alpha += ((x-self.x_ini) < 0) * math.pi
-            alpha += (alpha < 0) * 2*math.pi
-        if circxy:
-            cx, cy = self.coord_calc(alpha)
-            return alpha, cx, cy
-        else:
-            return alpha
-        
-    def coord_calc(self, alpha):
-        cx = self.r * np.cos(alpha) + self.x_ini
-        cy = self.r * np.sin(alpha) + self.y_ini
-        return cx, cy
-
-        
-    def create_circle(self, x, y, r, **kwargs):
-        
-        return self.canvas[self.notebook.index(
-            self.notebook.select())].create_oval(x-r, y-r, x+r, y+r, **kwargs)
-    
-    def create_circle_arc(self, x, y, r, **kwargs):
-        
-        if "start" in kwargs and "end" in kwargs:
-            kwargs["extent"] = kwargs["end"] - kwargs["start"]
-            # if self.clock == 'countclock':
-            #     kwargs["extent"] += (kwargs["extent"] < 0) * 360
-            del kwargs["end"]
-        return self.canvas[self.notebook.index(
-            self.notebook.select())].create_arc(x-r, y-r, x+r, y+r, **kwargs)
\ No newline at end of file
diff --git a/aidesign/GUI/plugins/ManualInput.py b/aidesign/GUI/plugins/ManualInput.py
deleted file mode 100644
index 5f1c50be..00000000
--- a/aidesign/GUI/plugins/ManualInput.py
+++ /dev/null
@@ -1,285 +0,0 @@
-from . import UI
-import tkinter as tk
-import os
-from PIL import Image, ImageTk
-from tkinter import messagebox, ttk
-from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
-import numpy as np
-import pandas as pd
-
-
-class ManualInput(tk.Frame,UI):
-    def __init__(self, parent, controller, config:dict):
-        self.parent = parent    
-        super().__init__(parent, bg = self.parent['bg'])
-        self.controller = controller
-        self.controller.title('Manual Input')
-        
-        
-        self.dirpath = os.path.dirname(os.path.realpath(__file__))
-        self.assets_path = os.path.join(self.dirpath,'resources','Assets')
-
-        self._class_list = None        
-        pixels = 500
-        path = os.path.join(self.dirpath,'resources','example_radiography_images')
-        self.N = len(os.listdir(path))
-        
-        self.image_list = []
-        for f in os.listdir(path):
-            self.image_list.append(
-                ImageTk.PhotoImage(self.expand2square(
-                    Image.open(os.path.join(path, f)), 
-                    (0, 0, 0)).resize((pixels, pixels)))) # (0, 0, 0) is the padding colour
-        
-        # Status bar in the lower part of the window
-        status = tk.Label(self, text='Image 1 of '+str(self.N), bd = 1, 
-                          relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
-                          bg = self.parent['bg'])
-        status.grid(row=20, column=0, columnspan=4, pady = 10, 
-                    sticky = tk.W+tk.E)
-        
-        # Inital window
-        self.my_label = tk.Label(self, image = self.image_list[0], 
-                                 bg = self.parent['bg'])
-        self.my_label.grid(column = 0, row = 0, rowspan = 10, columnspan = 3)
-    
-        # Buttons initialisation
-        self.back_img = ImageTk.PhotoImage(Image.open(
-            os.path.join(self.assets_path,'back_arrow.png')).resize((150, 50)))
-        self.forw_img = ImageTk.PhotoImage(Image.open(
-            os.path.join(self.assets_path,'forw_arrow.png')).resize((150, 50)))
-        self.button_back = tk.Button(
-            self, image = self.back_img, bg = self.parent['bg'], 
-            state = tk.DISABLED).grid(column = 0,row = 19)
-        self.button_save = tk.Button(
-            self, text = 'Save', fg = 'white', bg = self.parent['bg'], height = 3, 
-            width = 20, command = self.save_file).grid(column = 1,row = 19)
-        self.button_forw = tk.Button(
-            self, image = self.forw_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(2)).grid(column = 2,row = 19)
-        
-        button_main = tk.Button(
-            self, text="Done", 
-            fg = 'white', bg = self.parent['bg'], height = 3, width = 20, 
-            command = self.check_quit).grid(column = 4,row = 19)
-        self._parse_config(config)
-        self.save_path = ''
-        self.saved = True
-        
-    def _parse_config(self, config):
-        self.config = config
-        self.class_list(self.config["plugin"]["options"]["class_list"])
-
-    def class_list(self):
-        """Getter for required _class_list variable
-        
-        :return: list of class labels
-        :rtype: list of strings
-        """
-        return self._class_list
-
-    def class_list(self,value):
-        """Setter for required _class_list variable
-        
-        :param value: class labels for binary classification
-        :type value: list of strings
-        """
-        self._class_list = value
-        self.out_data = np.zeros((self.N, len(self._class_list)))
-        self.button_cl = {}
-        
-        self.var = {}
-        for i,cl in enumerate(self._class_list):
-            self.var[0,i] = tk.IntVar(value=self.out_data[0,i])
-            self.button_cl[cl] = tk.Checkbutton(
-                self, text = cl, fg = 'white', bg = self.parent['bg'], 
-                selectcolor = 'black', height = 3, width = 20, 
-                variable = self.var[0,i], 
-                command=(lambda i=i: self.onPress(0,i)))
-            self.button_cl[cl].grid(column = 4,row = i)
-        
-        #Tree defintion. Output display
-        style = ttk.Style()
-        style.configure(
-            "Treeview", background = 'white', foreground = 'white', 
-            rowheight = 25, fieldbackground = 'white', 
-            font = self.controller.pages_font)
-        style.configure("Treeview.Heading", font = self.controller.pages_font)
-        style.map('Treeview', background = [('selected', 'grey')])
-        
-        tree_frame = tk.Frame(self)
-        tree_frame.grid(row = 0, column = 5, columnspan = 4, rowspan = 10)
-        
-        tree_scrollx = tk.Scrollbar(tree_frame, orient = 'horizontal')
-        tree_scrollx.pack(side = tk.BOTTOM, fill = tk.X)
-        tree_scrolly = tk.Scrollbar(tree_frame)
-        tree_scrolly.pack(side = tk.RIGHT, fill = tk.Y)
-        
-        self.tree = ttk.Treeview(tree_frame, 
-                                 yscrollcommand = tree_scrolly.set, 
-                                 xscrollcommand = tree_scrollx.set)
-        self.tree.pack()
-        
-        tree_scrollx.config(command = self.tree.xview)
-        tree_scrolly.config(command = self.tree.yview)
-        
-        self.tree['columns'] = self._class_list
-        
-        # Format columns
-        self.tree.column("#0", width = 50)
-        for n, cl in enumerate(self._class_list):
-            self.tree.column(
-                cl, width = int(self.controller.pages_font.measure(str(cl)))+20, 
-                minwidth = 50, anchor = tk.CENTER)
-        # Headings
-        self.tree.heading("#0", text = "Image", anchor = tk.CENTER)
-        for cl in self._class_list:
-            self.tree.heading(cl, text = cl, anchor = tk.CENTER)
-        self.tree.tag_configure('odd', foreground = 'black', 
-                                background='#E8E8E8')
-        self.tree.tag_configure('even', foreground = 'black', 
-                                background='#DFDFDF')
-        # Add data
-        for n, sample in enumerate(self.out_data):
-            if n%2 == 0:
-                self.tree.insert(parent = '', index = 'end', iid = n, text = n+1, 
-                                 values = tuple(sample.astype(int)), tags = ('even',))
-            else:
-                self.tree.insert(parent = '', index = 'end', iid = n, text = n+1, 
-                                 values = tuple(sample.astype(int)), tags = ('odd',))
-        
-        # Select the current row
-        self.tree.selection_set(str(int(0)))
-        
-        # Define double-click on row action
-        self.tree.bind("<Double-1>", self.OnDoubleClick)
-
-    def expand2square(self, pil_img, background_color):
-        
-        " Adds padding to make the image a square."
-        width, height = pil_img.size
-        if width == height:
-            return pil_img
-        elif width > height:
-            result = Image.new(pil_img.mode, (width, width), background_color)
-            result.paste(pil_img, (0, (width - height) // 2))
-            return result
-        else:
-            result = Image.new(pil_img.mode, (height, height), 
-                               background_color)
-            result.paste(pil_img, ((height - width) // 2, 0))
-            return result
-
-    def check_quit(self):
-        
-        if not self.saved:
-            response = messagebox.askokcancel(
-                "Exit?", 
-                "Do you want to leave the program without saving?")
-            if response:
-                self.controller.destroy()
-        else:
-            response = messagebox.askokcancel(
-                "Exit?",
-                "Are you sure you are finished?")
-            self.controller.destroy()
-    
-    def open_file(self):
-        
-        self.save_path = askopenfile(mode='r+')
-        if self.save_path is not None:
-            t = self.save_path.read()
-            # textentry.delete('0.0', 'end')
-            # textentry.insert('0.0', t)
-            # textentry.focus()
-            
-    def save_file_as(self):
-        
-        self.save_path = asksaveasfile(mode='w')
-        self.save_file()
-        
-    def save_file(self):
-        
-        if self.save_path == '':
-            self.save_path = asksaveasfile(defaultextension = '.txt', 
-                                      filetypes = [('Text file', '.txt'), 
-                                                   ('CSV file', '.csv'), 
-                                                   ('All Files', '*.*')])
-        if self.save_path is not None: # asksaveasfile return `None` if dialog closed with "cancel".
-            filedata = pd.DataFrame(
-                self.out_data, columns = self._class_list).to_string()
-            self.save_path.seek(0) # Move to the first row to overwrite it
-            self.save_path.write(filedata)
-            self.save_path.flush() # Save without closing
-            # typically the above line would do. however this is used to ensure that the file is written
-            os.fsync(self.save_path.fileno())
-            self.saved = True
-            
-    def forward_back(self, image_number):
-        
-        " Forward button to continue to the next image in the folder."
-        
-        self.tree.selection_set(str(int(image_number-1)))
-        # Print the corresponding image
-        self.my_label.grid_forget()
-        self.my_label = tk.Label(self, image=self.image_list[image_number-1])
-        
-        # Update button commands
-        self.button_forw = tk.Button(
-            self, image = self.forw_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(
-                image_number+1)).grid(column = 2,row = 19)
-        self.button_back = tk.Button(
-            self, image = self.back_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(
-                image_number-1)).grid(column = 0,row = 19)
-        if image_number == self.N:
-            self.button_forw = tk.Button(
-                self, image = self.forw_img, bg = self.parent['bg'], 
-                state = tk.DISABLED).grid(column = 2,row = 19)
-        if image_number == 1:
-            self.button_back = tk.Button(
-                self, image = self.back_img, bg = self.parent['bg'], 
-                state = tk.DISABLED).grid(column = 0,row = 19)
-            
-        self.my_label.grid(column=0, row=0, rowspan = 10, columnspan=3)
-        
-        # Classes buttons
-        # var = {}
-        for i,cl in enumerate(self._class_list):
-            # print(out_data[image_number-1,i])
-            self.var[image_number-1,i] = tk.IntVar(
-                value = int(self.out_data[image_number-1,i]))
-            # var[i] = tk.IntVar(value = int(self.out_data[image_number-1,i]))
-            # I can not make this be selected when going backwards or forward 
-            # if it was previously selected.
-            self.button_cl[cl] = tk.Checkbutton(
-                self, text = cl, fg = 'white', bg = self.parent['bg'], 
-                selectcolor = 'black', height = 3, width = 20, 
-                variable = self.var[image_number-1,i], 
-                command=(lambda i=i: self.onPress(image_number-1,i)))
-            self.button_cl[cl].grid(column = 4,row = i)
-
-        # Status bar    
-        status = tk.Label(
-            self, text='Image ' + str(image_number) + ' of '+str(self.N), 
-            bd = 1, relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
-            bg = self.parent['bg'])
-        status.grid(row=20, column=0, columnspan=4, pady = 10, 
-                    sticky = tk.W+tk.E)
-            
-    def onPress(self, n,i):
-        
-        "Updates the stored values on clicking the checkbutton."
-        
-        self.out_data[n,i] = not self.out_data[n,i]
-        self.tree.item(self.tree.get_children()[n], text = n+1, 
-                       values = tuple(self.out_data[n,:].astype(int)))
-        self.saved = False
-        
-    def OnDoubleClick(self, event):
-        
-        "Moves to the image corresponding to the row clicked on the tree."
-        
-        item = self.tree.selection()[0]
-        self.forward_back(self.tree.item(item,"text"))
diff --git a/aidesign/GUI/plugins/aidCanvas.py b/aidesign/GUI/plugins/aidCanvas.py
deleted file mode 100644
index fd6e44be..00000000
--- a/aidesign/GUI/plugins/aidCanvas.py
+++ /dev/null
@@ -1,851 +0,0 @@
-# Import the required libraries
-import tkinter as tk
-from PIL import Image, ImageTk
-import os
-import numpy as np
-import pandas as pd
-from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
-from tkinter import messagebox
-
-from aidesign.Settings.Settings_core import Settings
-
-class aidCanvas(tk.Frame):
-
-    """ Creates a frame with a canvas and allows to include different modules
-    for display which translate into new modules defined for the framework."""
-    
-    def __init__(self, parent, controller, config:dict):
-        
-        " Here we define the main frame displayed upon opening the program."
-        " This leads to the different methods to provide feedback."
-        
-        super().__init__(parent, bg = parent['bg'])
-        self.bg = parent['bg']
-        self.controller = controller
-        
-        script_dir = os.path.dirname(__file__)
-        self.tk.call('wm','iconphoto', self.controller._w, ImageTk.PhotoImage(
-            file = os.path.join(os.path.join(
-                script_dir, 
-                'resources', 
-                'Assets', 
-                'AIDIcon.ico'))))
-        self.my_img1 = ImageTk.PhotoImage(Image.open(os.path.join(
-                script_dir, 
-                'resources', 
-                'Assets', 
-                'AIDIcon_name.png')).resize((250, 200)))
-        
-        self.my_label = tk.Label(self, image = self.my_img1, bg = parent['bg'])
-        self.my_label.grid(column = 5, row = 0)
-        
-        # Create canvas
-        self.width, self.height = 600, 600
-        self.canvas = tk.Canvas(self, width=self.width, 
-            height=self.height, background="white")
-        self.canvas.grid(row=0, column=0, columnspan=4, rowspan = 10, 
-                         padx = 10, pady = 10)
-        
-        self.canvas.startxy = []
-        self.out_data = pd.DataFrame()
-        self.connections = {}
-        self.modules = 0
-        self.module_list = []
-        self.module_names = []
-        
-        # Create module
-        self.w, self.h = 100, 50
-        self.cr = 4
-        
-        #Initialiser module
-        self.add_module('Initialiser', self.width/2, self.h, ini = True)
-        self.add_module('Output', self.width/2, self.height - self.h, out = True)
-        
-        self.draw = False
-        self.canvas.bind("<B1-Motion>", self.on_drag)
-        self.canvas.bind('<Button-1>', self.select)
-        self.l = 0 #number of loops
-        self.drawLoop = False
-        self.loops = []
-        
-        # self.canvas.bind("<ButtonPress-1>", self.on_button_press)
-        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
-        
-        
-        # for m, module in enumerate(modules):
-        tk.Button(
-            self, text = 'Data processing', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = lambda: self.add_module('DataProcessing', 
-                                              self.width/2, 
-                                              self.height/2)
-            ).grid(column = 5, row = 1)
-        tk.Button(
-            self, text = 'Modelling', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = lambda: self.add_module('Modelling', 
-                                              self.width/2, 
-                                              self.height/2)
-            ).grid(column = 5, row = 2)
-        tk.Button(
-            self, text = 'Decision making', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = lambda: self.add_module('DecisionMaking', 
-                                              self.width/2, 
-                                              self.height/2)
-            ).grid(column = 5, row = 3)
-        tk.Button(
-            self, text = 'User Feedback', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = lambda: self.add_module('UserFeedback', 
-                                              self.width/2, 
-                                              self.height/2)
-            ).grid(column = 5, row = 4)
-        tk.Button(
-            self, text = 'Input data', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = lambda: self.add_module('InputData', 
-                                              self.width/2, 
-                                              self.height/2)
-            ).grid(column = 5, row = 5)
-        tk.Button(
-            self, text = 'Delete selection', fg = 'white', bg = parent['bg'],
-            height = 3, width = 25, font = self.controller.pages_font,
-            command = self.delete_sel).grid(column = 5, row = 6)
-        tk.Button(
-            self, text = 'Upload', fg = 'white', bg = parent['bg'], 
-            height = 3, width = 15, font = self.controller.pages_font, 
-            command = self.upload).grid(column = 0, row = 10, sticky = tk.SE)
-        tk.Button(
-            self, text = 'Save', fg = 'white', bg = parent['bg'],
-            height = 3, width = 15, font = self.controller.pages_font, 
-            command = self.save_file).grid(column = 1, row = 10, sticky = tk.SW)
-        tk.Button(
-            self, text = 'Reset', fg = 'white', bg = parent['bg'], 
-            height = 3, width = 15, font = self.controller.pages_font, 
-            command = self.reset).grid(column = 2, row = 10, sticky = tk.SE)
-        tk.Button(
-            self, text = 'Done', fg = 'white', bg = parent['bg'], 
-            height = 3, width = 15, font = self.controller.pages_font, 
-            command = self.check_quit).grid(column = 3, row = 10, sticky = tk.SW)
-        
-        self.save_path = ''
-        self.saved = True
-
-    def class_list(self,value):
-        """ Temporary fix """
-        return value
-
-    def on_return_display(self, event):
-        """ Defines the type of loop and condition for the indicated loop
-        """
-        if self.loopDisp:
-            condition = self.entry1.get()
-            self.entry1.destroy()
-            if hasattr(self, 'entry2'):
-                self.entry2.focus()
-            key = 'type'
-            text_w = self.controller.pages_font.measure(condition) + 20
-            x = self.start_x + text_w/2
-        else:
-            condition = self.entry2.get()
-            self.entry2.destroy()
-            key = 'condition'
-            text_w = self.controller.pages_font.measure(condition) + 20
-            x = self.curX - text_w/2
-        self.loops[-1][key] = condition
-        self.canvas.create_text(
-            x, 
-            self.start_y + 20, 
-            font = self.controller.pages_font, 
-            text = condition, 
-            tags = ('loop-'+str(self.l-1), key+'-'+str(self.l-1)), 
-            justify = tk.CENTER)
-        self.canvas.tag_bind(key+'-'+str(self.l-1), 
-                         "<Double-1>", self.OnDoubleClick)
-        self.loopDisp = not self.loopDisp
-    
-    def on_button_release(self, event):
-        """ Finishes drawing the rectangle for loop definition. 
-            Once the rectangle is drawn, it stores the information of the 
-            modules inside the loop.
-        """
-        if self.drawLoop:
-            self.drawLoop = False
-            
-            if (abs(self.start_x - self.curX) > 10) and (
-                    abs(self.start_y - self.curY) > 10):
-                self.l += 1
-                # Identify modules in area
-                loopMod = self.canvas.find_enclosed(self.start_x, self.start_y, 
-                                                    self.curX, self.curY)
-                self.loops.append({'type': None,
-                                   'condition': None,
-                                   'mod': [], 
-                                   'coord': (self.start_x, self.start_y, 
-                                             self.curX, self.curY)})
-                for mod in loopMod:
-                    aux = self.canvas.itemcget(mod, 'tags').split(' ')[1]
-                    if len(aux) == 2 and aux[0] == 't':
-                        self.loops[-1]['mod'].append(self.canvas.itemcget(
-                            mod, 'text'))
-                
-                # Ask for loop definition and condition and display               
-                self.entry1 = tk.Entry(self.canvas, justify='center', 
-                                      font = self.controller.pages_font)
-                text = 'For/While'
-                self.loopDisp = True
-                self.entry1.insert(0, text)
-                # self.entry['selectbackground'] = '#d0d4d9'
-                self.entry1['exportselection'] = False
-        
-                self.entry1.focus()
-                self.entry1.bind("<Return>", self.on_return_display)
-                self.entry1.bind("<Escape>", lambda *ignore: self.entry1.destroy())
-                
-                self.entry1.place(
-                    x = self.start_x + 10,
-                    y = self.start_y + 20, 
-                    anchor = tk.W,
-                    width = self.controller.pages_font.measure(text)+10)
-                text = 'Condition'
-                self.entry2 = tk.Entry(self.canvas, justify='center', 
-                                      font = self.controller.pages_font)
-                self.entry2.insert(0, text)
-                self.entry2['exportselection'] = False
-        
-                self.entry2.bind("<Return>", self.on_return_display)
-                self.entry2.bind("<Escape>", lambda *ignore: self.entry.destroy())
-                
-                self.entry2.place(
-                    x = self.curX-self.controller.pages_font.measure(text)-20,
-                    y = self.start_y + 20, 
-                    anchor = tk.W,
-                    width = self.controller.pages_font.measure(text)+10)
-                
-                self.saved = False
-            else:
-                self.canvas.delete('loop-'+str(self.l))
-        else:
-            self.updateLoops()
-    
-    def updateLoops(self):
-        """ Checks if, after some movement, a module is included in an existing
-            loop
-        """
-        for l, loop in enumerate(self.loops):
-            coord = loop['coord']
-            loopMod = self.canvas.find_enclosed(coord[0], coord[1],
-                                                coord[2], coord[3])
-            self.loops[l]['mod'] = []
-            for mod in loopMod:
-                aux = self.canvas.itemcget(mod, 'tags').split(' ')[1]
-                if len(aux) == 2 and aux[0] == 't':
-                    self.loops[l]['mod'].append(self.canvas.itemcget(
-                        mod, 'text'))
-        print('Updated loop info:', self.loops)
-        
-    def select(self, event):
-        """ Selects the module at the mouse location. """
-        self.selected = self.canvas.find_overlapping(
-            event.x-5, event.y-5, event.x+5, event.y+5)
-        if self.selected:
-            if len(self.selected) > 2:
-                self.canvas.selected = self.selected[-2]
-            else:
-                self.canvas.selected = self.selected[-1]
-            # self.canvas.startxy = (event.x, event.y)
-        else:
-            self.canvas.selected = None
-            # save mouse drag start position
-            self.start_x = self.canvas.canvasx(event.x)
-            self.start_y = self.canvas.canvasy(event.y)
-            self.curX = self.start_x
-            self.curY = self.start_y
-            self.selected = self.canvas.find_overlapping(
-                        event.x-5, event.y-5, event.x+5, event.y+5)
-    
-            # create rectangle if not yet exist
-            if not self.selected:
-                self.rect = self.canvas.create_rectangle(event.x, event.y, 
-                                             event.x, event.y, 
-                                             outline = '#4ff07a',
-                                             tag = 'loop-'+str(self.l))
-                                             # ,fill = '#b3ffc7')
-                self.drawLoop = True
-                if self.l == 0:
-                    self.canvas.tag_lower('loop-'+str(self.l))
-                else:
-                    self.canvas.tag_raise('loop-'+str(self.l), 'loop-'+str(self.l-1))
-        
-        if len(self.canvas.gettags("current")) > 0:
-            if(len(self.canvas.gettags("current")[0].split('-')) > 1) and (
-                self.canvas.gettags("current")[0].split('-')[0] == 'loop'):
-                self.isLoop = True
-                self.m = int(self.canvas.gettags("current")[0].split('-')[1])
-            else:
-                self.isLoop = False
-                self.m = int(self.canvas.gettags("current")[0][1:])
-    
-    def on_drag(self, event):
-        """ Uses the mouse location to move the module and its text. 
-        At the same time, it looks up if there are any connection to this
-        module and subsequently moves the connection."""
-        
-        if self.drawLoop:
-            self.curX = self.canvas.canvasx(event.x)
-            self.curY = self.canvas.canvasy(event.y)
-    
-            # w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
-            # if event.x > 0.9*w:
-            #     self.canvas.xview_scroll(1, 'units') 
-            # elif event.x < 0.1*w:
-            #     self.canvas.xview_scroll(-1, 'units')
-            # if event.y > 0.9*h:
-            #     self.canvas.yview_scroll(1, 'units') 
-            # elif event.y < 0.1*h:
-            #     self.canvas.yview_scroll(-1, 'units')
-    
-            # expand rectangle as you drag the mouse
-            self.canvas.coords('loop-'+str(self.l), self.start_x, 
-                               self.start_y, self.curX, self.curY)
-        else:
-            self.select(event)
-            
-            self.m = int(self.canvas.gettags("current")[0][1:])
-            dx = event.x - self.canvas.startxy[self.m][0]
-            dy = event.y - self.canvas.startxy[self.m][1]
-            # if self.isLoop:
-                # self.canvas.move('loop-'+str(self.m), dx, dy) 
-            # else:
-            # module
-            self.canvas.move('o'+str(self.m), dx, dy)
-            # self.updateLoops()
-            # Connections
-            if any(self.out_data.iloc[self.m].values) or any(
-                    self.out_data[self.out_data.columns[self.m]].values):
-                out = np.arange(self.out_data.values.shape[0])[
-                    self.out_data.values[self.m, :]==1]
-                for o in out:
-                    xycoord_i = self.canvas.coords(
-                        self.connections[o][self.m].split('-')[0])
-                    xycoord_o = self.canvas.coords(
-                        self.connections[o][self.m].split('-')[1])
-                    self.canvas.coords(self.connections[o][self.m], 
-                            (xycoord_i[0] + self.cr, 
-                             xycoord_i[1] + self.cr, 
-                             xycoord_o[0] + self.cr, 
-                             xycoord_o[1] + self.cr))
-                inp = np.arange(self.out_data.values.shape[0])[
-                    self.out_data.values[:, self.m]==1]
-                for i in inp:
-                    xycoord_i = self.canvas.coords(
-                        self.connections[self.m][i].split('-')[0])
-                    xycoord_o = self.canvas.coords(
-                        self.connections[self.m][i].split('-')[1])
-                    self.canvas.coords(self.connections[self.m][i], 
-                            (xycoord_i[0] + self.cr, 
-                             xycoord_i[1] + self.cr, 
-                             xycoord_o[0] + self.cr, 
-                             xycoord_o[1] + self.cr))
-            # Update module location
-            self.canvas.startxy[self.m] = (event.x, event.y)
-    
-    def module_out(self, name):
-        """ Updates the output DataFrame.
-        
-        :param name: name of the model
-        :type name: str
-        """
-        name_list = list(self.out_data.columns)
-        m_num = [n for n in name_list if (n.split('-')[0] == name)]
-        if len(m_num) > 0:
-            name_list.append(name + '-' + str(len(m_num)))
-        else:
-            name_list.append(name)
-        self.canvas.itemconfig('t'+str(self.modules), text = name_list[-1])
-        values = self.out_data.values
-        values = np.vstack((
-                    np.hstack((values, np.zeros((values.shape[0],1)))),
-                    np.zeros((1, values.shape[0]+1))))
-        self.out_data = pd.DataFrame(values, 
-                                     columns = name_list, 
-                                     index = name_list)
-        self.module_names.append(name_list[-1])
-
-    def add_module(self, boxName, x, y, ini = False, out = False):
-        """ Creates a rectangular module with the corresponding text inside.
-        
-        :param boxName: name of the model
-        :type boxName: str
-        """
-        if not ini and not out:
-            tag = ('o'+str(self.modules),)
-        else: #Make initialisation and output unmoveable
-            tag = ('n0',)
-        text_w = self.controller.pages_font.measure(boxName+'-00') + 10
-        self.canvas.create_rectangle(
-            x - text_w/2 , 
-            y - self.h/2, 
-            x + text_w/2, 
-            y + self.h/2, 
-            tags = tag + ('p'+str(self.modules),), 
-            fill = self.bg, width = 3,
-            activefill = '#dbaa21')
-        self.canvas.create_text(
-            x, 
-            y, 
-            font = self.controller.pages_font, 
-            text = boxName, 
-            tags = tag + ('t'+str(self.modules),), 
-            fill = '#d0d4d9', 
-            justify = tk.CENTER)
-        self.canvas.tag_bind('t'+str(self.modules), 
-                             "<Double-1>", self.OnDoubleClick)
-        if not out:
-            self.canvas.create_oval(
-                x - self.cr, 
-                y + self.h/2 - self.cr, 
-                x + self.cr, 
-                y + self.h/2 + self.cr, 
-                width = 2, 
-                fill = 'black', 
-                tags = tag + ('d'+str(self.modules),))
-            self.canvas.tag_bind('d'+str(self.modules), 
-                                 "<Button-1>", self.join_modules)
-            
-        if not ini:
-            self.canvas.create_oval(
-                x - self.cr, 
-                y - self.h/2 - self.cr, 
-                x + self.cr, 
-                y - self.h/2 + self.cr, 
-                width = 2, 
-                fill = 'black', 
-                tags = tag + ('u'+str(self.modules),))
-            self.canvas.tag_bind('u'+str(self.modules), 
-                                 "<Button-1>", self.join_modules)
-        
-        if not out and not ini:
-            self.canvas.create_oval(
-                x - text_w/2 - self.cr, 
-                y - self.cr, 
-                x - text_w/2 + self.cr, 
-                y + self.cr, 
-                width = 2, 
-                fill = 'black', 
-                tags = tag + ('l'+str(self.modules),))
-            self.canvas.tag_bind('l'+str(self.modules), 
-                                 "<Button-1>", self.join_modules)
-        
-            self.canvas.create_oval(
-                x + text_w/2 - self.cr, 
-                y - self.cr, 
-                x + text_w/2 + self.cr, 
-                y + self.cr, 
-                width = 2, 
-                fill = 'black', 
-                tags = tag + ('r'+str(self.modules),))
-            self.canvas.tag_bind('r'+str(self.modules), 
-                                 "<Button-1>", self.join_modules)
-        self.canvas.startxy.append((x, 
-                                    y))
-        self.connections[self.modules] = {}
-        self.module_out(boxName)
-        self.module_list.append(boxName)
-        self.modules += 1
-        self.saved = False
-    
-    def on_return_rename(self, event):
-        """ This function renames a module to the input specified through 
-        an entry widget. It then updates the corresponding rows and columns in
-        the stored data."""
-        
-        moduleName = self.entry.get()
-        if (moduleName in list(self.module_names)) and not(
-                moduleName == list(self.module_names)[self.m]):
-            messagebox.showwarning("Error", "This module already exists.")
-        else:
-            self.canvas.itemconfig('t'+str(self.m), text = moduleName)
-            x0, y0, x1, y1 = self.canvas.coords('p'+str(self.m))
-            text_w = self.controller.pages_font.measure(moduleName) + 20
-            shift = (text_w-x1+x0)/2
-            self.canvas.coords('p'+str(self.m), # Resize rectangle
-                               x0 - shift, 
-                               y0, 
-                               x1 + shift, 
-                               y1)
-            x0, y0, x1, y1 = self.canvas.coords('l'+str(self.m))
-            self.canvas.coords('l'+str(self.m), # Move left circle
-                                x0 - shift, 
-                                y0, 
-                                x1 - shift, 
-                                y1)
-            x0, y0, x1, y1 = self.canvas.coords('r'+str(self.m))
-            self.canvas.coords('r'+str(self.m), # Move left circle
-                                x0 + shift, 
-                                y0, 
-                                x1 + shift, 
-                                y1)
-            self.entry.destroy()
-            self.module_names[self.m] = moduleName
-            self.saved = False
-            
-    def on_return_editLoop(self, event):
-        """ This function renames loop conditions and updates the loop
-        information."""
-        
-        newText = self.entry.get()
-        conditions = self.canvas.itemcget(
-                        self.selected[0], 'tags').split(' ')[1].split('-')
-        self.canvas.itemconfig(self.selected[0], text = newText)
-        self.entry.destroy()
-        self.loops[int(conditions[1])][conditions[0]] = newText
-        self.saved = False
-        
-    def OnDoubleClick(self, event):
-        
-        """ Executed when text is double clicked.
-        Opens an entry box to edit the module name and updates the display and
-        the stored data. """
-        
-        self.selected = self.canvas.find_overlapping(
-            event.x-5, event.y-5, event.x+5, event.y+5)
-        
-        # If not loop text
-        if len(self.canvas.itemcget(
-                self.selected[0], 'tags').split(' ')[-2].split('-')) < 2:
-            
-            x0, y0, x1, y1 = self.canvas.coords(self.canvas.gettags("current")[0])
-            if hasattr(self, 'entry'):
-                self.entry.destroy()
-            
-            entryText = self.canvas.itemcget('t'+str(self.m), 'text')
-            
-            self.entry = tk.Entry(self.canvas, justify='center', 
-                                  font = self.controller.pages_font)
-    
-            self.entry.insert(
-                0, entryText)
-            self.entry['selectbackground'] = '#d0d4d9'
-            self.entry['exportselection'] = False
-    
-            self.entry.focus_force()
-            self.entry.bind("<Return>", self.on_return_rename)
-            self.entry.bind("<Escape>", lambda *ignore: self.entry.destroy())
-            
-            self.entry.place(x = x0, 
-                              y = y0 + (y1-y0)/2, 
-                              anchor = tk.W, width = x1 - x0)
-        # If loop text
-        else:
-            x0, y0 = self.canvas.coords(self.selected[0])
-            if hasattr(self, 'entry'):
-                self.entry.destroy()
-
-            # condition = self.canvas.itemcget(
-            #             self.selected[0], 'tags').split(' ')[-2]#.split('-')[0]
-            entryText = self.canvas.itemcget(self.selected[0], 'text')
-            
-            self.entry = tk.Entry(self.canvas, justify='center', 
-                                  font = self.controller.pages_font)
-            self.entry.insert(0, entryText)
-            # self.entry1['selectbackground'] = '#d0d4d9'
-            self.entry['exportselection'] = False
-    
-            self.entry.focus()
-            self.entry.bind("<Return>", self.on_return_editLoop)
-            self.entry.bind("<Escape>", lambda *ignore: self.entry.destroy())
-            text_w = self.controller.pages_font.measure(entryText) + 20
-            self.entry.place(x = x0 - text_w/2, 
-                          y = y0, 
-                          anchor = tk.W, 
-                          width = text_w)
-                
-    def delete_sel(self):
-        """ Deletes last selected module"""
-        if self.canvas.selected:
-            if self.isLoop:
-                self.canvas.delete('loop-'+str(self.m))
-                self.loops[self.m] = -1
-            elif not(self.m == 0):
-                self.canvas.delete('o'+str(self.m))
-                
-                col = self.out_data.columns[self.m]
-                self.out_data[col] = 0
-                self.out_data.loc[col] = 0
-                self.saved = False
-                self.module_names[self.m] = None
-                
-    def join_modules(self, event):
-        """ Draws a connecting line between two connecting circles. """
-        if self.draw:
-            tags = self.canvas.gettags(self.canvas.find_overlapping(
-            event.x-self.cr, event.y-self.cr, 
-            event.x+self.cr, event.y+self.cr)[-1])
-            tag2 = tags[-2] if tags[-1] == "current" else tags[-1]
-            if tag2[0] in ['d', 'l', 'u', 'r'] and int(
-                    tag2[1:]) != int(self.tag[1:]):
-                self.canvas.create_line(
-                            self.canvas.linestartxy[0] + self.cr, 
-                            self.canvas.linestartxy[1] + self.cr, 
-                            self.canvas.coords(tag2)[0] + self.cr, 
-                            self.canvas.coords(tag2)[1] + self.cr,
-                            fill = "red", 
-                            arrow = tk.LAST, 
-                            tags = ('o'+str(int(self.tag[1:])), 
-                                  'o'+str(int(tag2[1:])), self.tag + '-' + tag2))
-                self.out_data.iloc[int(self.tag[1:])][int(tag2[1:])] = 1
-                self.connections[int(tag2[1:])][
-                    int(self.tag[1:])] = self.tag + '-' + tag2
-            self.draw = False
-            self.saved = False
-        else:
-            tags = self.canvas.gettags(self.canvas.find_overlapping(
-            event.x-self.cr, event.y-self.cr, 
-            event.x+self.cr, event.y+self.cr)[-1])
-            self.tag = tags[-2] if tags[-1] == "current" else tags[-1]
-            self.canvas.linestartxy = self.canvas.coords(self.tag)#(event.x, event.y)
-            self.draw = True
-
-    def save_file_as(self):
-        
-        self.save_path = asksaveasfile(mode='w')
-        self.save_file()
-        
-    def save_file(self):
-        
-        if self.save_path == '':
-            self.save_path = asksaveasfile(defaultextension = '.xml', filetypes = 
-                                      [('XML file', '.xml'), 
-                                       ('All Files', '*.*')])
-        if self.save_path is not None: # asksaveasfile return `None` if dialog closed with "cancel".
-            # Transform input for output file
-            data = self.out_data.copy()
-            if np.sum(data.values[0,:]) == 0:
-                messagebox.showwarning(
-                    "Error", "Initialiser is not connected to any module.")
-            elif np.sum(data.values[:,1]) == 0:
-                messagebox.showwarning(
-                    "Error", "Output is not connected to any module.")
-            idx = (np.sum(data.values, axis = 0) 
-                   + np.sum(data.values, axis = 1)) < 1
-            col = np.array(data.columns)
-            for c in np.arange(len(idx))[idx]:
-                data = data.drop(columns=col[c], index=col[c])
-                
-            loop_modules = np.unique([v for a in self.loops for v in a['mod']])
-            out_loops = np.zeros_like(self.loops)
-            
-            values = data.values.astype(bool)
-            mn = np.array(self.module_names)
-            mn_id = [m is not None for m in mn]
-            mn = mn[mn_id]
-            s = Settings()
-            s.new_config_file(self.save_path.name)
-            s.filename = self.save_path.name
-            s.append_pipeline_module(self.module_list[0], # Initialiser
-                                  mn[0],
-                                  "",
-                                  {},
-                                  list(mn[values[:,0]]),
-                                  list(mn[values[0,:]]),
-                                  None,
-                                  [self.canvas.startxy[0], 0, self.connections[0]])
-            for i, mnn in enumerate(mn_id):
-                if (i > 1) and mnn:
-                    xml_parent = None
-                    parent_loops = []
-                    if mn[i] in loop_modules: # Model in any loop
-                        for x, loop in enumerate(self.loops):
-                            if mn[i] in loop['mod']: # Model in this loop
-                                if not out_loops[x]: # Loop already defined
-                                    s.append_pipeline_loop(self.loops[x]['type'],
-                                                self.loops[x]['condition'],
-                                                "loop"+str(x),
-                                                parent_loops,
-                                                list(loop['mod']),
-                                                xml_parent,
-                                                self.loops[x]['coord']
-                                                )
-                                    out_loops[x] = 1
-                                xml_parent = "loop"+str(x) # parent is the last loop
-                                parent_loops.append("loop"+str(x))
-
-                    s.append_pipeline_module(self.module_list[i],
-                      mn[i],
-                      "",
-                      {},
-                      list(mn[values[:,i]]),
-                      list(mn[values[i,:]]),
-                      xml_parent,
-                      [self.canvas.startxy[i], i, self.connections[i]])
-                
-            s.append_pipeline_module(self.module_list[1], # Out
-                      mn[1],
-                      "",
-                      {},
-                      list(mn[values[:,1]]),
-                      list(mn[values[1,:]]),
-                      None,
-                      [self.canvas.startxy[1], 1, self.connections[1]])
-            s.write_to_XML()
-            self.saved = True
-            self.controller._append_to_output("xml_filename",self.save_path.name)
-
-    def upload(self):
-        
-        filename = askopenfilename(initialdir = os.getcwd(), 
-                                   title = 'Select a file', 
-                                   defaultextension = '.xml', 
-                                   filetypes = [('XML file', '.xml'), 
-                                                ('All Files', '*.*')])
-        if filename is not None:
-            self.reset()
-
-            s = Settings()
-            s.load_XML(filename)
-            s._print_pretty(s.loaded_modules)
-            modules = s.loaded_modules
-            modout = modules['output']
-            del modules['Initialiser'], modules['output'] # They are generated when resetting
-            disp_mod = ['Initialiser', 'output']
-            id_mod = [0, 1]
-            
-            # Place the modules
-            disp_mod, id_mod = self.place_modules(modules, id_mod, disp_mod)
-            connect = list(modout['coordinates'][2].keys())
-            for p, parent in enumerate(modout['parents']):
-                    parent_id = id_mod[np.where(np.array(disp_mod) == parent)[0][0]]
-                    out, ins = modout['coordinates'][2][connect[p]].split('-')
-                    xout, yout, _ , _ = self.canvas.coords(out[0]+str(parent_id))
-                    xins, yins, _, _ =self.canvas.coords(ins[0]+str(1))
-                    self.canvas.create_line(
-                                xout + self.cr, 
-                                yout + self.cr, 
-                                xins + self.cr, 
-                                yins + self.cr,
-                                fill = "red", 
-                                arrow = tk.LAST, 
-                                tags = ('o'+str(parent_id), 
-                                      'o'+str(1), modout['coordinates'][2][connect[p]]))
-                    self.out_data.iloc[int(parent_id)][1] = 1
-                    self.connections[1][
-                        int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(1)
-            self.controller._append_to_output("xml_filename",filename)
-
-    def place_modules(self, modules, id_mod, disp_mod):
-        # Place the modules
-        for key in [key for key, val in modules.items() if type(val) == dict]:
-            if modules[key]['class'] == 'loop':
-                # Extracts numbers from string
-                l = int(''.join(map(str, list(filter(str.isdigit, modules[key]['name'])))))
-                x0, y0, x1, y1 = modules[key]['coordinates']
-                self.canvas.create_rectangle(x0, y0, 
-                                             x1, y1, 
-                                             outline = '#4ff07a',
-                                             tag = 'loop-'+str(l))
-                text_w = self.controller.pages_font.measure(modules[key]['type']) + 20
-                self.canvas.create_text(
-                    x0 + text_w/2, 
-                    y0 + 20, 
-                    font = self.controller.pages_font, 
-                    text = modules[key]['type'], 
-                    tags = ('loop-'+str(l), 'type'+'-'+str(l)), 
-                    justify = tk.CENTER)
-                text_w = self.controller.pages_font.measure(modules[key]['condition']) + 20
-                self.canvas.create_text(
-                    x1 - text_w/2, 
-                    y0 + 20, 
-                    font = self.controller.pages_font, 
-                    text = modules[key]['condition'], 
-                    tags = ('loop-'+str(l), 'condition'+'-'+str(l)), 
-                    justify = tk.CENTER)
-                self.loops.append({'type': modules[key]['type'],
-                                   'condition': modules[key]['condition'],
-                                   'mod': [], 
-                                   'coord': (x0, y0, 
-                                             x1, y1)})
-                disp_mod, id_mod = self.place_modules(modules[key], id_mod, disp_mod)
-            else:
-                # Display module
-                self.add_module(key,
-                                modules[key]['coordinates'][0][0],
-                                modules[key]['coordinates'][0][1])
-                id_mod.append(modules[key]['coordinates'][1])
-                connect = list(modules[key]['coordinates'][2].keys())
-                
-                # Connect modules
-                for p, parent in enumerate(modules[key]['parents']):
-                    if not (parent[:4] == 'loop'):
-                        parent_id = id_mod[np.where(np.array(disp_mod) == parent)[0][0]]
-                        out, ins = modules[key]['coordinates'][2][connect[p]].split('-')
-                        xout, yout, _ , _ = self.canvas.coords(out[0]+str(parent_id))
-                        xins, yins, _, _ =self.canvas.coords(ins[0]+str(id_mod[-1]))
-                        self.canvas.create_line(
-                                    xout + self.cr, 
-                                    yout + self.cr, 
-                                    xins + self.cr, 
-                                    yins + self.cr,
-                                    fill = "red", 
-                                    arrow = tk.LAST, 
-                                    tags = ('o'+str(parent_id), 
-                                          'o'+str(id_mod[-1]), modules[key]['coordinates'][2][connect[p]]))
-                        self.out_data.iloc[int(parent_id)][int(id_mod[-1])] = 1
-                        self.connections[int(id_mod[-1])][
-                            int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(id_mod[-1])
-                    else:
-                        self.loops[-1]['mod'].append(key)
-                disp_mod.append(key)
-        return disp_mod, id_mod
-    
-    def reset(self):
-        
-        if not self.saved:
-            msg = messagebox.askyesnocancel(
-                'Info', 'Are you sure you want to reset the canvas?')
-        else:
-            msg = True
-        if msg:
-            self.canvas.delete(tk.ALL) # Reset canvas
-            
-            if hasattr(self, 'entry'):
-                self.entry.destroy()
-            if hasattr(self, 'entry1'):
-                self.entry1.destroy()
-            if hasattr(self, 'entry2'):
-                self.entry2.destroy()
-                
-            self.canvas.startxy = []
-            self.out_data = pd.DataFrame()
-            self.connections = {}
-            self.modules = 0
-            self.module_list = []
-            self.module_names = []
-            
-            self.add_module('Initialiser', self.width/2, self.h, ini = True)
-            self.add_module('Output', self.width/2, self.height - self.h, out = True)
-        
-            self.draw = False
-            self.loops = []
-            self.drawLoop = False
-            self.l = 0
-
-    def check_quit(self):
-        
-        if not self.saved:
-            response = messagebox.askokcancel(
-                "Are you sure you want to leave?", 
-                "Do you want to leave the program without saving?")
-            if response:
-                if self.save_path not in [None, '']:
-                    self.controller.XML.set(True)
-                self.controller._show_frame("MainPage")
-        else:
-            if self.save_path not in [None, '']:
-                    self.controller.XML.set(True)
-            self.controller._show_frame("MainPage")
-
-if __name__ == "__main__":
-    app = aidCanvas()
-    app.mainloop()
\ No newline at end of file
diff --git a/aidesign/GUI/plugins/resources/Assets/ADDIcon.ico b/aidesign/GUI/plugins/resources/Assets/ADDIcon.ico
deleted file mode 100644
index 9f78df37..00000000
Binary files a/aidesign/GUI/plugins/resources/Assets/ADDIcon.ico and /dev/null differ
diff --git a/aidesign/GUI/plugins/resources/Assets/ADDIcon_name.png b/aidesign/GUI/plugins/resources/Assets/ADDIcon_name.png
deleted file mode 100644
index ba3d51d6..00000000
Binary files a/aidesign/GUI/plugins/resources/Assets/ADDIcon_name.png and /dev/null differ
diff --git a/aidesign/GUI/plugins/resources/Assets/AIDIcon.ico b/aidesign/GUI/plugins/resources/Assets/AIDIcon.ico
deleted file mode 100644
index e3064fa8..00000000
Binary files a/aidesign/GUI/plugins/resources/Assets/AIDIcon.ico and /dev/null differ
diff --git a/aidesign/GUI/plugins/resources/Assets/AIDIcon_name.png b/aidesign/GUI/plugins/resources/Assets/AIDIcon_name.png
deleted file mode 100644
index 629fc3d0..00000000
Binary files a/aidesign/GUI/plugins/resources/Assets/AIDIcon_name.png and /dev/null differ
diff --git a/aidesign/GUI/plugins/resources/Assets/AIFRED.png b/aidesign/GUI/plugins/resources/Assets/AIFRED.png
deleted file mode 100644
index 3cd5a6ba..00000000
Binary files a/aidesign/GUI/plugins/resources/Assets/AIFRED.png and /dev/null differ
diff --git a/aidesign/InputData/Modelling_core.py b/aidesign/InputData/Modelling_core.py
new file mode 100644
index 00000000..97a81d24
--- /dev/null
+++ b/aidesign/InputData/Modelling_core.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+class Modelling(object):
+    def __init__(self):
+        self.node_name = None
+        self.plugin_name = None
+        self.output_data = None
+
+    def set_input_data(self, data):
+        self.input_data = data
+
+    def set_target_data(self, data):
+        self.target_data = data
+
+    def _load_plugin(self):
+        pass
+
+    def set_plugin_name(self, plugin_name):
+        self.plugin_name = plugin_name
+        self._load_plugin()
+
+    def set_options(self, options):
+        self.options = options
+
+    def solve(self):
+        # send the input data to the modelling plugin and assign it to an output property
+        pass
+
+    def get_result(self):
+        return self.output_data
diff --git a/aidesign/InputData/__init__.py b/aidesign/InputData/__init__.py
new file mode 100644
index 00000000..99e78a66
--- /dev/null
+++ b/aidesign/InputData/__init__.py
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+
+"""Import all submodules inside this folder and expose them with their respective names
+
+Let's look for a more automatic way of doing this when we have more modules
+"""
+from .Modelling_core import Modelling
diff --git a/aidesign/Modelling/plugins/regression.py b/aidesign/Modelling/plugins/regression.py
index 8a60ca35..7a811859 100644
--- a/aidesign/Modelling/plugins/regression.py
+++ b/aidesign/Modelling/plugins/regression.py
@@ -3,6 +3,13 @@
 import numpy as np
 from matplotlib import pyplot as plt
 
+_PLUGIN_CLASS_NAME = "Regression"
+_PLUGIN_CLASS_DESCRIPTION = "Regression ML model placeholder"
+_PLUGIN_READABLE_NAMES = {"regression":"default","reg":"alias"}
+_PLUGIN_MODULE_OPTIONS = {}
+_PLUGIN_REQUIRED_SETTINGS = {}
+_PLUGIN_OPTIONAL_SETTINGS = {}
+
 class Regression(object):
     def __init__(self):
         self.input_data = None
@@ -37,24 +44,24 @@ def predict(self,data):
         return self.regression_function.predict(data)
         
 
-reg = Regression()
+# reg = Regression()
 
-X_train = np.array([1,2,3,4,5,6,7,8,9])
-y = X_train ** 5
-print(X_train)
-print(y)
+# X_train = np.array([1,2,3,4,5,6,7,8,9])
+# y = X_train ** 5
+# print(X_train)
+# print(y)
 
-reg.set_input_data(X_train)
-reg.fit_degree = 1
-reg.set_target_data(y)
-reg.solve()
+# reg.set_input_data(X_train)
+# reg.fit_degree = 1
+# reg.set_target_data(y)
+# reg.solve()
 
-X_test = X_train
-pred = reg.predict(X_test)
-print(pred)
+# X_test = X_train
+# pred = reg.predict(X_test)
+# print(pred)
 
-plt.figure()
-plt.plot(X_test,pred,c='r',label="Test Data. Degree: {0}".format(reg.fit_degree))
-plt.scatter(X_train,y,label="Training Data")
-plt.legend()
-plt.show()
\ No newline at end of file
+# plt.figure()
+# plt.plot(X_test,pred,c='r',label="Test Data. Degree: {0}".format(reg.fit_degree))
+# plt.scatter(X_train,y,label="Training Data")
+# plt.legend()
+# plt.show()
\ No newline at end of file
diff --git a/aidesign/Settings/Settings_core.py b/aidesign/Settings/Settings_core.py
index 0e51a2e1..70339ee2 100644
--- a/aidesign/Settings/Settings_core.py
+++ b/aidesign/Settings/Settings_core.py
@@ -16,6 +16,7 @@ def __init__(self, filename: str = None):
 
         """valid_tags lists the available XML tags and their function
         TODO: populate the modules in this list automatically
+        TODO: Can extend the plugin_helpers.py script to automatically populate these things
         """
         self._valid_tags = {
             "pipeline": "declaration",
@@ -306,6 +307,52 @@ def _add_coords(self,
         coords_elem.text = str("\n{0}".format(coords))
         return elem
 
+    def _add_plugin_options(self, 
+                            plugin_elem:ET.Element,
+                            options
+                            ):
+        for key in options.keys():
+            if isinstance(options[key], list):
+                new_option = ET.SubElement(plugin_elem, key)
+                option_text = ("\n{}".format(
+                    "\n".join([*options[key]])))
+                new_option.text = option_text
+            elif isinstance(options[key], (int,float,str)):
+                new_option = ET.SubElement(plugin_elem, key)
+                text_lead = "\n" if "\n" not in str(options[key]) else ""
+                new_option.text = "{0} {1}".format(text_lead, str(options[key]))
+            elif isinstance(options[key], (dict)):
+                self._add_plugin_options(plugin_elem,options[key])
+
+    def append_plugin_to_module(self,
+                                plugin_type: str,
+                                plugin_options: dict,
+                                xml_parent: ET.Element or str,
+                                overwrite_existing: bool = False
+                                ):
+        """Appened plugin as subelement to existing module element
+        
+        :param plugin_type: string type of plugin to be loaded into module
+        :param plugin_options: dict where keys & values are options & values
+        :param xml_parent: dict OR str. 
+                            If string given, parent elem is found via search,
+                            Otherwise, plugin appeneded directly
+        """
+        if isinstance(xml_parent,str):
+            xml_parent = self._get_element_from_name(xml_parent)
+            
+        plugin_elem = xml_parent.find("./plugin")
+
+        if plugin_elem is not None and overwrite_existing:
+            xml_parent.remove(plugin_elem)
+            plugin_elem = None
+
+        if plugin_elem is None:
+            plugin_elem = ET.SubElement(xml_parent, "plugin")
+            plugin_elem.set('type', plugin_type) 
+        self._add_plugin_options(plugin_elem,plugin_options)
+        
+
     def append_pipeline_module(self,
                                 module_type: str,
                                 module_name: str,
@@ -332,18 +379,11 @@ def append_pipeline_module(self,
         new_mod = ET.Element(module_type.replace(" ", ""))
         new_mod.set('name', module_name)
 
-        new_plugin = ET.SubElement(new_mod, "plugin")
-        new_plugin.set('type', plugin_type)
-
-        for key in plugin_options.keys():
-            new_option = ET.SubElement(new_plugin, key)
-            if isinstance(plugin_options[key], list):
-                option_text = ("\n{}".format(
-                    "\n".join([*plugin_options[key]])))
-                new_option.text = option_text
-
-            elif isinstance(plugin_options[key], str):
-                new_option.set('value', plugin_options[key])
+        self.append_plugin_to_module(plugin_type,
+                                        plugin_options,
+                                        new_mod,
+                                        0
+                                        )
 
         if xml_parent_element.tag =="loop":
             parents.append(xml_parent_element.attrib["name"])
@@ -415,7 +455,9 @@ def append_data_structure_field(self,
     # s.new_config_file("./resources/example_config.xml")
     # s._get_all_elements_with_tag("loop")
     # s.load_XML("./resources/example_config.xml")
+    # s.append_plugin_to_module("Input Data Plugin",{"option":{"test":4}},"Input data",1)
     # s.print_loaded_modules()
+    
     # s.write_to_XML()
     # s.append_pipeline_loop("for",
     #                       "10",
diff --git a/aidesign/Settings/__init__.py b/aidesign/Settings/__init__.py
index a51c2a4a..45c739c2 100644
--- a/aidesign/Settings/__init__.py
+++ b/aidesign/Settings/__init__.py
@@ -4,5 +4,5 @@
 
 Let's look for a more automatic way of doing this when we have more modules
 """
-from .Settings_core import Settings
-from .. import utils
\ No newline at end of file
+# from .Settings_core import Settings
+# from .. import utils
\ No newline at end of file
diff --git a/aidesign/Settings/resources/Hospital.xml b/aidesign/Settings/resources/Hospital.xml
index 11c7649a..3891f275 100644
--- a/aidesign/Settings/resources/Hospital.xml
+++ b/aidesign/Settings/resources/Hospital.xml
@@ -66,7 +66,7 @@
                     (322, 207)
                 </coordinates>
             </InputData>
-            <UserFeedbackAdaptation name="User Feedback Adaptation">
+            <UserFeedback name="User Feedback Adaptation">
                 <plugin type="" />
                 <relationships>
                     <parent name="Decision making" />
@@ -76,7 +76,7 @@
                 <coordinates>
                     (454, 325)
                 </coordinates>
-            </UserFeedbackAdaptation>
+            </UserFeedback>
             <DecisionMaking name="Decision making">
                 <plugin type="" />
                 <relationships>
@@ -101,4 +101,4 @@
         </Output>
     </pipeline>
     <datastructure />
-</Settings>
+</Settings>
\ No newline at end of file
diff --git a/aidesign/Settings/resources/basic_operation.xml b/aidesign/Settings/resources/basic_operation.xml
index dc4bf3d7..a19fba06 100644
--- a/aidesign/Settings/resources/basic_operation.xml
+++ b/aidesign/Settings/resources/basic_operation.xml
@@ -27,7 +27,7 @@
                     <parent name="Init" />
                     <child name="Data Processing 1" />
                 </relationships>
-        </UserFeedback>
+            </UserFeedback>
             <loop type="for" condition="1" name="For Loop 2">
                 <UserFeedback name="My Second UserFeedback Module">
                     <plugin type="canvas">
diff --git a/aidesign/UserFeedback/User_Feedback_core.py b/aidesign/UserFeedback/UserFeedback_core.py
similarity index 89%
rename from aidesign/UserFeedback/User_Feedback_core.py
rename to aidesign/UserFeedback/UserFeedback_core.py
index f1a165e0..1b893a02 100644
--- a/aidesign/UserFeedback/User_Feedback_core.py
+++ b/aidesign/UserFeedback/UserFeedback_core.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 import tkinter as tk
-
+from tkinter import font  as tkfont
 from aidesign.utils.import_helper import import_plugin
 
 class UserFeedback(tk.Tk):
@@ -13,10 +13,10 @@ class UserFeedback(tk.Tk):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        self.title_font = tk.font.Font(family='Helvetica',
+        self.title_font = tkfont.Font(family='Helvetica',
                                       size=14,
                                       weight="bold")
-        self.pages_font = tk.font.nametofont("TkDefaultFont")
+        self.pages_font = tkfont.nametofont("TkDefaultFont")
 
         self.desired_ui_types = []
         self.top_ui_layer = None
@@ -81,10 +81,13 @@ def _set_plugin_name(self, ui_type: list):
         ui_type = ui_type\
             if isinstance(ui_type, list)\
             else [ui_type]
-
         for ui in ui_type:
+            print(ui)
+            # ui_name = ''.join(kn for kn in self.available_ui_types.keys()
+            #                   if ui.lower() == self.available_ui_types[kn]["name"])
             ui_name = ''.join(kn for kn in self.available_ui_types.keys()
-                              if ui.lower() == self.available_ui_types[kn]["name"])
+                              if self.available_ui_types[kn]["name"] in ui.lower())
+            print(ui_name)
             self._add_UI_type_to_frames(ui_name)
 
 
@@ -106,7 +109,7 @@ def launch(self):
         """Runs UserInterface Plugin. 
         If multiple frames exist, they are stacked
         """
-        container = tk.Frame(self, bg='#19232d')
+        container = tk.Frame(self, bg='#064663')
         container.pack(side="top", fill="both", expand=True)
         container.grid_rowconfigure(0, weight=1)
         container.grid_columnconfigure(0, weight=1)
diff --git a/aidesign/UserFeedback/__init__.py b/aidesign/UserFeedback/__init__.py
index 67725339..fd002947 100644
--- a/aidesign/UserFeedback/__init__.py
+++ b/aidesign/UserFeedback/__init__.py
@@ -4,6 +4,6 @@
 
 Let's look for a more automatic way of doing this when we have more modules
 """
-from .. import utils
-from .User_Feedback_core import UserFeedback
-from .User_Feedback_template import UI
+# from .. import utils
+# from .UserFeedback_core import UserFeedback
+# from .User_Feedback_template import UI
diff --git a/aidesign/UserFeedback/plugins/CanvasInput.py b/aidesign/UserFeedback/plugins/CanvasInput.py
index 23e7ab0d..585d5a52 100644
--- a/aidesign/UserFeedback/plugins/CanvasInput.py
+++ b/aidesign/UserFeedback/plugins/CanvasInput.py
@@ -1,628 +1,651 @@
-from . import UI
-import tkinter as tk
-import os
-
-from tkinter import messagebox, ttk
-
-from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
-import numpy as np
-import pandas as pd
-import math
-
-class CanvasInput(tk.Frame,UI):
-    def __init__(self, parent, controller, config:dict):
-        self.parent = parent
-        super().__init__(self.parent, bg = self.parent['bg'])
-        self.controller = controller
-        
-        self.controller.title('Canvas Input')
-        self.notebook = ttk.Notebook(self)
-        self.notebook.grid(row=0, column=0, columnspan=7, 
-                           rowspan = 12, pady = 15)
-        self.frame = []
-        self.tree = []
-        self.canvas = []
-        self.draw = []
-        self.state = []
-        self.type = []
-        self.clock = []
-        self._parse_config(config)
-        self.save_path = ''
-        self.saved = True
-    
-    def _parse_config(self, config):
-        self.config = config
-        self.class_list(self.config["plugin"]["options"]["class_list"])
-
-    def class_list(self):
-        """Getter for required _class_list variable
-        
-        :return: list of class labels
-        :rtype: list of strings
-        """
-        return self._class_list
-
-    def class_list(self, value):
-        """Setter for required _class_list variable
-        
-        :param value: labels for state-action pair headers
-        :type value: list of strings
-        """
-        self._class_list = value
-        self.out_data = {}
-        for ii in np.arange(len(self._class_list)):
-            self.out_data[ii] = {}
-            for key in self._class_list[ii]:
-                self.out_data[ii][key] = []
-
-        for ii in np.arange(len(self._class_list)):
-            self.frame.append(tk.Frame(self, bg = self.parent['bg']))
-            self.frame[-1].grid(row=0, column=0, sticky="nsew", pady = 15)
-            if self._class_list[ii][0].split('_')[1] == 'a':
-                self.type.append('Rotating')
-                self.clock.append(tk.StringVar())
-                self.clock[ii].set('clock')
-            else:
-                self.type.append('Sliding')
-                self.clock.append(-1)
-            self.notebook.add(
-                self.frame[-1], 
-                text = 'Object '+ str(ii) + ' - ' + self.type[-1])
-                
-            
-            # Create a canvas widget
-            self.width, self.height = 600, 600
-            self.canvas.append(tk.Canvas(
-                self.frame[-1], width=self.width, 
-                height=self.height, background="white"))
-            self.canvas[-1].grid(row=0, column=0, columnspan=4, rowspan = 2)
-            self.canvas[-1].bind('<Button-1>', self.draw_dot)
-            self.canvas[-1].bind("<B1-Motion>", self.on_drag)
-            # self.canvas[-1].bind('<Motion>', self.motion)
-            
-            # Booleans to identify what the user wants to do
-            self.draw.append(tk.StringVar())
-            self.draw[ii].set('draw')
-            self.state.append(tk.StringVar())
-            self.state[ii].set('state')    
-            # Buttons under the canvas
-            self.button_draw = tk.Radiobutton(
-                self.frame[-1], text = 'Draw', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.draw[ii], 
-                selectcolor = 'black', value = 'draw').grid(column = 4, row = 3)
-            self.button_drag = tk.Radiobutton(
-                self.frame[-1], text = 'Move', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.draw[ii], 
-                selectcolor = 'black', value = 'drag').grid(column = 5, row = 3)
-            self.clock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Clockwise', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.clock[ii], 
-                selectcolor = 'black', value = 'clock').grid(column = 6, row = 3)            
-            self.countclock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Counterclockwise', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, var = self.clock[ii], 
-                selectcolor = 'black', value = 'countclock').grid(column = 7, row = 3)
-            # self.button_edit = tk.Radiobutton(
-            #     self.frame[-1], text = 'Edit', fg = 'white', bg = self.parent['bg'],
-            #     height = 3, width = 20, var = self.draw[ii], 
-            #     selectcolor = 'black', value = 'edit').grid(column = 6,row = 3)
-            self.button_save = tk.Button(
-                self.frame[-1], text = 'Save', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, 
-                command = self.save_file).grid(column = 1, row = 3)
-            self.button_upload = tk.Button(
-                self.frame[-1], text = 'Upload coordinates', fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20, 
-                command = self.upload_sa).grid(column = 0, row = 3)
-            self.button_reset = tk.Button(
-                self.frame[-1], text = 'Reset', fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20, 
-                command = self.reset).grid(column = 2, row = 3)
-            button_main = tk.Button(
-                self.frame[-1], text="Done", fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20,
-                command = self.check_quit).grid(column = 3, row = 3)
-            style = ttk.Style()
-            style.configure(
-                "Treeview", background = 'white', foreground = 'white', 
-                rowheight = 25, fieldbackground = 'white', 
-                font = self.controller.pages_font)
-            style.configure("Treeview.Heading", 
-                            font = self.controller.pages_font)
-            style.map('Treeview', background = [('selected', 'grey')])
-            
-
-            tree_frame = tk.Frame(self.frame[-1])
-            tree_frame.grid(row = 0, column = 4, columnspan = 3, rowspan = 10)
-            
-            tree_scrollx = tk.Scrollbar(tree_frame, orient = 'horizontal')
-            tree_scrollx.pack(side = tk.BOTTOM, fill = tk.X)
-            tree_scrolly = tk.Scrollbar(tree_frame)
-            tree_scrolly.pack(side = tk.RIGHT, fill = tk.Y)
-            
-            self.tree.append(ttk.Treeview(
-                tree_frame, 
-                yscrollcommand = tree_scrolly.set, 
-                xscrollcommand = tree_scrollx.set))
-            self.tree[-1].pack()
-            
-            tree_scrollx.config(command = self.tree[-1].xview)
-            tree_scrolly.config(command = self.tree[-1].yview)
-            
-            if self.type[-1] == 'Rotating':
-                self.x_ini, self.y_ini = int(self.width/2), int(self.height/2)
-                self.r = int(np.min([self.x_ini, self.y_ini])/1.5)
-                self.create_circle(self.x_ini, self.y_ini, 4, 
-                                    fill="#DDD", outline="", width=4)
-                self.create_circle(self.x_ini, self.y_ini, self.r, 
-                                    fill="", outline="#DDD", width=4)
-                
-            self.tree[-1]['columns'] = self._class_list[ii]
-            
-            # Format columns
-            self.tree[-1].column("#0", width = 50)
-            for n, cl in enumerate(self._class_list[ii]):
-                self.tree[-1].column(
-                    cl, width = int(
-                        self.controller.pages_font.measure(str(cl)))+20, 
-                    minwidth = 50, anchor = tk.CENTER)
-                    
-            # Headings
-            self.tree[-1].heading("#0", text = "Sample", anchor = tk.CENTER)
-            for cl in self._class_list[ii]:
-                self.tree[-1].heading(cl, text = cl, anchor = tk.CENTER)
-            
-            # for nn in np.arange(N):
-            #     self.tree[-1].insert(
-            #                 parent = '', index = 'end', iid = (nn+1)*1000, text = 'Trial '+str(nn), 
-            #                 values = tuple())
-            
-            self.tree[-1].tag_configure('odd', foreground = 'black', 
-                                        background='#E8E8E8')
-            self.tree[-1].tag_configure('even', foreground = 'black', 
-                                        background='#DFDFDF')
-        
-            # Define double-click on row action
-            self.tree[-1].bind("<Double-1>", self.OnDoubleClick)
-
-    def draw_dot(self, event):
-        
-        ii = self.notebook.index(self.notebook.select())
-        if self.draw[ii].get() == 'drag':
-            self.selected = self.canvas[ii].find_overlapping(
-                event.x-10, event.y-10, event.x+10, event.y+10)
-            if self.selected:
-                if len(self.selected) > 2:
-                    self.canvas[ii].selected = self.selected[-2]
-                else:
-                    self.canvas[ii].selected = self.selected[-1]
-                self.canvas[ii].startxy = (event.x, event.y)
-            else:
-                self.canvas[ii].selected = None
-
-        if self.draw[ii].get()  == 'draw':
-            # Draw an oval in the given coordinates
-            self.saved = False
-            # Angle of the selected point with respect to the circle center
-            if self.type[ii] == 'Rotating':
-                alpha, x_o, y_o = self.angle_calc(event.x, event.y, 
-                                                  circxy=True)
-                if self.state[ii].get()  == 'state': # Write state coordinates
-                    self.canvas[ii].create_oval(
-                        x_o-3, y_o-3, x_o+3, y_o+3, fill="black", width=0, 
-                        tags=("state"+str(ii)+'-'
-                              + str(len(self.out_data[ii]['State_a']))))
-                    self.out_data[ii]['State_a'].append((
-                        -np.rad2deg(alpha)+360)%360)
-                    self.state[ii].set('action')
-                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
-                        n = int(self.tree[ii].selection()[0]) + 1
-                        if n%2 == 0:
-                            tag = ('even',)
-                        else:
-                            tag = ('odd',)
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = n, text = n+1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[n,:].astype(int)), 
-                            tags = tag)
-                        self.tree[ii].selection_set(str(n))
-                    else:
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = 0, text = 1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[0,:].astype(int)), 
-                                tags = ('even',))
-                        self.tree[ii].selection_set(str(0))
-                    
-                elif self.state[ii].get()  == 'action':
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['State_a'][-1]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['State_a'][-1] - 360
-                        end = 360 - np.rad2deg(alpha)
-                    self.create_circle_arc(
-                        self.x_ini, self.y_ini, self.r, fill = "", 
-                        outline = "red", start = start, end = end, width=2, 
-                        style = tk.ARC, tags = ("action"+str(ii)+'-' + str(len(
-                            self.out_data[ii]['Action_a']))))              
-                    self.out_data[ii]['Action_a'].append((
-                        -np.rad2deg(alpha)+360)%360)
-                    self.state[ii].set('state')
-                    n = int(self.tree[ii].selection()[0])
-                    self.tree[ii].item(
-                        self.tree[ii].get_children()[n], text = n+1, 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])[n,:].astype(int)))
-            else:
-                if self.state[ii].get()  == 'state': # Write state coordinates
-                    self.canvas[ii].create_oval(
-                        event.x-3, event.y-3, event.x+3, event.y+3, 
-                        fill="black", width=0, 
-                        tags=("state"+str(ii)+'-'+ str(len(
-                            self.out_data[ii]['State_x']))))
-                    self.out_data[ii]['State_x'].append(event.x)
-                    self.out_data[ii]['State_y'].append(event.y)
-                    self.state[ii].set('action')
-                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
-                        n = int(self.tree[ii].selection()[0]) + 1
-                        if n%2 == 0:
-                            tag = ('even',)
-                        else:
-                            tag = ('odd',)
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = n, text = n+1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[n,:].astype(int)), 
-                            tags = tag)
-                        self.tree[ii].selection_set(str(n))
-                    else:
-                        self.tree[ii].insert(
-                            parent = '', index = 'end', iid = 0, text = 1, 
-                            values = tuple(self.dict2mat(
-                                self.out_data[ii])[0,:].astype(int)), 
-                                tags = ('even',))
-                        self.tree[ii].selection_set(str(0))
-                    
-                elif self.state[ii].get()  == 'action':
-                    self.canvas[ii].create_line(
-                        self.out_data[ii]['State_x'][-1], 
-                        self.out_data[ii]['State_y'][-1], event.x, event.y, 
-                        fill="red", arrow=tk.LAST, 
-                        tags=("action"+str(ii)+'-' + str(len(
-                            self.out_data[ii]['Action_x']))))
-                    self.out_data[ii]['Action_x'].append(event.x)
-                    self.out_data[ii]['Action_y'].append(event.y)
-                    self.state[ii].set('state')
-                    n = int(self.tree[ii].selection()[0])
-                    self.tree[ii].item(
-                        self.tree[ii].get_children()[n], text = n+1, 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])[n,:].astype(int)))
-        
-    def on_drag(self, event):
-        
-        ii = self.notebook.index(self.notebook.select())
-        if self.draw[ii].get()  == 'drag' and self.canvas[ii].selected:
-            # move the selected item
-            n = int(self.canvas[ii].gettags("current")[0].split('-')[1])
-            
-            if self.type[ii] == 'Rotating':
-                alpha, dx, dy = self.angle_calc(event.x, event.y, circxy=True)
-                _, dx_pr, dy_pr = self.angle_calc(
-                    self.canvas[ii].startxy[0], self.canvas[ii].startxy[1], 
-                    circxy=True)
-                xa = self.r * np.cos(
-                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.x_ini
-                ya = self.r * np.sin(
-                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.y_ini
-                if self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
-                    self.canvas[ii].move(
-                        self.canvas[ii].selected, dx-dx_pr, 
-                        dy-dy_pr)
-                    self.out_data[ii]['State_a'][n] = (360-np.rad2deg(alpha))%360
-
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['Action_a'][n]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['Action_a'][n] - 360
-                        end = 360 - np.rad2deg(alpha)
-
-                    self.canvas[ii].itemconfigure(
-                        "action"+str(ii)+'-'+str(n), start = start, 
-                        extent = end - start)
-                    
-                elif self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
-                    
-                    self.out_data[ii]['Action_a'][n] = (360-np.rad2deg(alpha))%360
-
-                    if self.clock[ii].get()  == 'clock':
-                        start = self.out_data[ii]['State_a'][n]
-                        end = np.rad2deg(-alpha)
-                    else:
-                        start = self.out_data[ii]['State_a'][n] - 360
-                        end = 360 - np.rad2deg(alpha)
-                    
-                    self.canvas[ii].itemconfigure(
-                        "action"+str(ii)+'-'+str(n), start = start, 
-                        extent = end - start)
-
-            else:
-                # calculate distance moved from last position
-                dx = event.x - self.canvas[ii].startxy[0]
-                dy = event.y - self.canvas[ii].startxy[1]
-                if self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
-                    self.canvas[ii].move(self.canvas[ii].selected, dx, dy)
-                    self.canvas[ii].coords(
-                        "action"+str(ii)+'-'+str(n), 
-                        (event.x, event.y, self.out_data[ii]['Action_x'][n], 
-                        self.out_data[ii]['Action_y'][n]))
-                    self.out_data[ii]['State_x'][n] = event.x
-                    self.out_data[ii]['State_y'][n] = event.y  
-                elif self.canvas[ii].gettags(
-                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
-                    self.canvas[ii].coords(
-                        self.canvas[ii].gettags("current")[0], 
-                        (self.out_data[ii]['State_x'][n], 
-                         self.out_data[ii]['State_y'][n], event.x, event.y))
-                    self.out_data[ii]['Action_x'][n] = event.x
-                    self.out_data[ii]['Action_y'][n] = event.y
-            # update last position
-            self.canvas[ii].startxy = (event.x, event.y)                
-            self.tree[ii].item(self.tree[ii].get_children()[n], text = n+1, 
-                           values = tuple(self.dict2mat(
-                               self.out_data[ii])[n,:].astype(int)))
-            self.tree[ii].selection_set(str(n))
-
-    def checkered(self, line_distance):
-        
-        ii = self.notebook.index(self.notebook.select()) 
-        # vertical lines at an interval of "line_distance" pixel
-        for x in range(line_distance, self.width, line_distance):
-            self.canvas[ii].create_line(x, 0, x, self.height, fill="#476042")
-        # horizontal lines at an interval of "line_distance" pixel
-        for y in range(line_distance, self.height, line_distance):
-            self.canvas[ii].create_line(0, y, self.width, y, fill="#476042")   
-            
-    def check_quit(self):
-        if not self.saved:
-            response = messagebox.askokcancel(
-                "Exit?", 
-                "Do you want to leave the program without saving?")
-            if response:
-                self.controller.destroy()
-        else:
-            response = messagebox.askokcancel(
-                "Exit?",
-                "Are you sure you are finished?")
-            self.controller.destroy()
-            
-    def save_file_as(self):
-        self.save_path = asksaveasfile(mode='w')
-        self.save_file()
-        
-    def save_file(self):
-        
-        if self.save_path == '':
-            self.save_path = asksaveasfile(defaultextension = '.txt', filetypes = 
-                                      [('Text file', '.txt'), 
-                                       ('CSV file', '.csv'), 
-                                       ('All Files', '*.*')])
-        if self.save_path is not None: # asksaveasfile return `None` if dialog closed with "cancel".
-            # Transform input for output file    
-            data = {}
-            samples = np.max([len(self.out_data[element][
-                list(self.out_data[element].keys())[0]]) 
-                for element in self.out_data])
-            for ii in self.out_data.keys():
-                for c in self.out_data[ii].keys():
-                    a, b = c.split('_')
-                    aux = self.out_data[ii][c]
-                    aux.extend([-1]*(samples - len(self.out_data[ii][c])))
-                    data[a+str(ii)+'_'+b] = aux
-                    
-            filedata = pd.DataFrame(data, columns = data.keys()).to_string()
-            self.save_path.seek(0) # Move to the first row to overwrite it
-            self.save_path.write(filedata)
-            self.save_path.flush() # Save without closing
-            # typically the above line would do. however this is used to ensure that the file is written
-            os.fsync(self.save_path.fileno())
-            self.saved = True
-         
-    def on_return(self, event):
-        ii = self.notebook.index(self.notebook.select())
-        val = self.tree[ii].item(self.treerow)['values']
-        val = [float(i) for i in val]
-        val[int(self.treecol[1:])-1] = float(self.entry.get())
-        self.tree[ii].item(self.treerow, values = val)
-        self.entry.destroy()        
-        self.saved = False
-        
-        if self.type[ii] == 'Rotating':
-            dx, dy = self.coord_calc(np.deg2rad(360 - val[0]))
-            dx_pr, dy_pr = self.coord_calc(np.deg2rad(360 - self.out_data[ii]['State_a'][self.treerow]))            
-            self.out_data[ii]['State_a'][self.treerow] = float(val[0])
-            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx-dx_pr, 
-                dy-dy_pr)
-            
-            self.out_data[ii]['Action_a'][self.treerow] = float(val[1])
-            if self.clock[ii].get()  == 'clock':
-                start = self.out_data[ii]['State_a'][self.treerow]
-                end = self.out_data[ii]['Action_a'][self.treerow]
-            else:
-                start = self.out_data[ii]['State_a'][self.treerow] - 360
-                end = self.out_data[ii]['Action_a'][self.treerow]
-            
-            self.canvas[ii].itemconfigure(
-                "action"+str(ii)+'-'+str(self.treerow), start = start, 
-                extent = end - start)
-        else:
-            # calculate distance moved from last position
-            dx_s = val[0] - self.out_data[ii]['State_x'][self.treerow]
-            dy_s = val[1] - self.out_data[ii]['State_y'][self.treerow]
-
-            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx_s, dy_s)
-
-            self.out_data[ii]['State_x'][self.treerow] = val[0]
-            self.out_data[ii]['State_y'][self.treerow] = val[1]
-
-            self.canvas[ii].coords(
-                "action"+str(ii)+'-'+str(self.treerow), 
-                (self.out_data[ii]['State_x'][self.treerow], 
-                  self.out_data[ii]['State_y'][self.treerow], val[2], val[3]))
-            self.out_data[ii]['Action_x'][self.treerow] = val[2]
-            self.out_data[ii]['Action_y'][self.treerow] = val[3]
-            
-    def OnDoubleClick(self, event):
-        
-        """ Executed when a row is double clicked.
-        Opens an entry box to edit a cell and updates the canvas and the 
-        stored data. """
-        
-        ii = self.notebook.index(self.notebook.select())
-        self.treerow = int(self.tree[ii].identify_row(event.y))
-        self.treecol = self.tree[ii].identify_column(event.x)
-        
-        # get column position info
-        x, y, width, height = self.tree[ii].bbox(self.treerow, self.treecol)
-    
-        # y-axis offset
-        pady = height // 2
-        # pady = 0
-        
-        if hasattr(self, 'entry'):
-            self.entry.destroy()
-            
-        self.entry = tk.Entry(self.tree[ii], justify='center') 
-        
-        if int(self.treecol[1:]) > 0:
-            self.entry.insert(
-                0, self.tree[ii].item(self.treerow)['values'][int(str(self.treecol[1:]))-1])
-            # self.entry['selectbackground'] = '#123456'
-            self.entry['exportselection'] = False
-    
-            self.entry.focus_force()
-            self.entry.bind("<Return>", self.on_return)
-            self.entry.bind("<Escape>", lambda *ignore: self.entry.destroy())
-            
-            self.entry.place(x = x, 
-                             y = y + pady, 
-                             anchor = tk.W, width = width)
-
-    def upload_sa(self):
-        
-        ii = self.notebook.index(self.notebook.select())
-        filename = askopenfilename(initialdir = os.getcwd(), 
-                                   title = 'Select a file', 
-                                   defaultextension = '.txt', 
-                                   filetypes = [('Text file', '.txt'), 
-                                                ('CSS file', '.css'), 
-                                                ('All Files', '*.*')])
-        if filename is not None:
-            data = open(filename,'r')
-            read = False
-            self.draw[ii].set('drag')
-            for n, point in enumerate(data):
-                if read: # Not elegant at all, just to omit the header.
-                    i, sx, sy, ax, ay = point.split()
-                    # Draw an oval in the given coordinates
-                    self.canvas[ii].create_oval(
-                        float(sx)-3, float(sy)-3, float(sx)+3, float(sy)+3, 
-                        fill="black", width=0, 
-                        tags=("state-" + str(len(
-                            self.out_data[ii]['State_x']))))
-                    self.canvas[ii].create_line(
-                        float(sx), float(sy), float(ax), float(ay), 
-                        fill="red", arrow=tk.LAST, 
-                        tags=("action"+str(ii)+"-"+ str(len(
-                            self.out_data[ii]['Action_x']))))
-                    self.out_data[ii]['State_x'].append(sx)
-                    self.out_data[ii]['State_y'].append(sy)
-                    self.out_data[ii]['Action_x'].append(ax)
-                    self.out_data[ii]['Action_y'].append(ay)
-                    self.tree.insert(
-                        parent = '', index = 'end', 
-                        iid = len(self.out_data[ii]['Action_x'])-1, 
-                        text = len(self.out_data[ii]['Action_x']), 
-                        values = tuple(self.dict2mat(
-                            self.out_data[ii])
-                            [len(self.out_data[ii]
-                                 ['Action_x'])-1,:].astype(int)))
-                    self.tree.selection_set(str(len(
-                        self.out_data[ii]['Action_x'])-1))
-                else:
-                    read = True
-    
-    def reset(self):
-        
-        ii = self.notebook.index(self.notebook.select())
-        msg = messagebox.askyesnocancel(
-            'Info', 'Are you sure you want to reset the canvas?')
-        if msg:
-            self.canvas[ii].delete(tk.ALL) # Reset canvas
-            self.state[ii].set('state')
-            self.out_data[ii] = {}
-            for key in self._class_list[ii]:
-                self.out_data[ii][key] = []
-                    
-            for record in self.tree[ii].get_children(): #Reset treeview
-                self.tree[ii].delete(record)
-            if self.type[ii] == 'Rotating': 
-                self.create_circle(self.x_ini, self.y_ini, 4, 
-                                   fill="#DDD", outline="", width=4)
-                self.create_circle(self.x_ini, self.y_ini, self.r, 
-                                   fill="", outline="#DDD", width=4)
-    
-    def dict2mat(self, X):
-        
-        max_size = 0
-        for key in X:
-            if len(X[key]) > max_size:
-                max_size = len(X[key])
-
-        result = -np.ones((max_size, len(X)))
-        for k, key in enumerate(X):
-            result[:len(X[key]), k]  = X[key]
-            
-        return result
-    
-    def angle_calc(self, x, y, circxy = False):
-        if (x == self.x_ini) & (y < self.y_ini):
-            alpha = math.pi/2
-        elif (x == self.x_ini) & (y > self.y_ini):
-            alpha = 3*math.pi/2
-        else:
-            alpha = np.arctan((y-self.y_ini)/(x-self.x_ini))
-            alpha += ((x-self.x_ini) < 0) * math.pi
-            alpha += (alpha < 0) * 2*math.pi
-        if circxy:
-            cx, cy = self.coord_calc(alpha)
-            return alpha, cx, cy
-        else:
-            return alpha
-        
-    def coord_calc(self, alpha):
-        cx = self.r * np.cos(alpha) + self.x_ini
-        cy = self.r * np.sin(alpha) + self.y_ini
-        return cx, cy
-
-        
-    def create_circle(self, x, y, r, **kwargs):
-        
-        return self.canvas[self.notebook.index(
-            self.notebook.select())].create_oval(x-r, y-r, x+r, y+r, **kwargs)
-    
-    def create_circle_arc(self, x, y, r, **kwargs):
-        
-        if "start" in kwargs and "end" in kwargs:
-            kwargs["extent"] = kwargs["end"] - kwargs["start"]
-            # if self.clock == 'countclock':
-            #     kwargs["extent"] += (kwargs["extent"] < 0) * 360
-            del kwargs["end"]
-        return self.canvas[self.notebook.index(
+from . import UI
+import tkinter as tk
+import os
+
+from tkinter import messagebox, ttk
+
+from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
+import numpy as np
+import pandas as pd
+import math
+
+_PLUGIN_CLASS_NAME = "CanvasInput"
+_PLUGIN_CLASS_DESCRIPTION = "Method of user feedback for state-action pairs"
+_PLUGIN_READABLE_NAMES = {"canvas":"default","state-action":"alias","robot":"alias"}
+_PLUGIN_MODULE_OPTIONS = {}
+_PLUGIN_REQUIRED_SETTINGS = {"class_list":"list"}
+_PLUGIN_OPTIONAL_SETTINGS = {}
+class CanvasInput(tk.Frame,UI):
+    def __init__(self, parent, controller, config:dict):
+        self.parent = parent
+        super().__init__(self.parent, bg = self.parent['bg'])
+        self.controller = controller
+        
+        self.controller.title('Canvas Input')
+        self.notebook = ttk.Notebook(self)
+        self.notebook.pack(fill = tk.BOTH, expand = True)
+        self.frame = []
+        self.tree = []
+        self.canvas = []
+        self.draw = []
+        self.state = []
+        self.type = []
+        self.clock = []
+        self._parse_config(config)
+        self.save_path = ''
+        self.saved = True
+        self.grid_rowconfigure(0, weight=1)
+        self.grid_columnconfigure(0, weight=1)
+
+    def _parse_config(self, config):
+        self.config = config
+        self.class_list(self.config["plugin"]["options"]["class_list"])
+
+    def class_list(self):
+        """Getter for required _class_list variable
+        
+        :return: list of class labels
+        :rtype: list of strings
+        """
+        return self._class_list
+
+    def class_list(self, value):
+        """Setter for required _class_list variable
+        
+        :param value: labels for state-action pair headers
+        :type value: list of strings
+        """
+        self._class_list = value
+        self.out_data = {}
+        for ii in np.arange(len(self._class_list)):
+            self.out_data[ii] = {}
+            for key in self._class_list[ii]:
+                self.out_data[ii][key] = []
+
+        for ii in np.arange(len(self._class_list)):
+            self.frame.append(tk.Frame(self, bg = self.parent['bg']))
+            self.frame[-1].pack(fill = tk.BOTH, expand = True)#grid(row=0, column=0, sticky="nsew", pady = 15)
+            if self._class_list[ii][0].split('_')[1] == 'a':
+                self.type.append('Rotating')
+                self.clock.append(tk.StringVar())
+                self.clock[ii].set('clock')
+            else:
+                self.type.append('Sliding')
+                self.clock.append(-1)
+            self.notebook.add(
+                self.frame[-1], 
+                text = 'Object '+ str(ii) + ' - ' + self.type[-1])
+            
+            # Frames
+            self.frame1 = tk.Frame(self.frame[-1], bg = self.parent['bg'])
+            tree_frame = tk.Frame(self.frame[-1], bg = self.parent['bg'])
+            frame3 = tk.Frame(self.frame[-1], bg = self.parent['bg'])
+            self.frame4 = tk.Frame(self.frame[-1], bg = self.parent['bg'])
+
+            # Create a canvas widget
+            self.width, self.height = 600, 600
+            self.canvas.append(tk.Canvas(
+                self.frame1, width=self.width, 
+                height=self.height, background="white"))
+            self.canvas[-1].pack(fill = tk.BOTH, expand = True, padx=(10,0), pady=(10,0))
+            self.canvas[-1].bind('<Button-1>', self.draw_dot)
+            self.canvas[-1].bind("<B1-Motion>", self.on_drag)
+            # self.canvas[-1].bind('<Motion>', self.motion)
+            
+            # Booleans to identify what the user wants to do
+            self.draw.append(tk.StringVar())
+            self.draw[ii].set('draw')
+            self.state.append(tk.StringVar())
+            self.state[ii].set('state')    
+            # Buttons under the canvas
+            self.button_draw = tk.Radiobutton(
+                self.frame4, text = 'Draw', fg = 'white', bg = self.parent['bg'],
+                height = 3, width = 20, var = self.draw[ii], 
+                selectcolor = 'black', value = 'draw').grid(column = 4, row = 3, sticky="news", pady=(0,10))
+            self.button_drag = tk.Radiobutton(
+                self.frame4, text = 'Move', fg = 'white', bg = self.parent['bg'],
+                height = 3, width = 20, var = self.draw[ii], 
+                selectcolor = 'black', value = 'drag').grid(column = 5, row = 3, sticky="news", pady=(0,10))
+            self.clock_arc = tk.Radiobutton(
+                self.frame4, text = 'Clockwise', fg = 'white', bg = self.parent['bg'],
+                height = 3, width = 20, var = self.clock[ii], 
+                selectcolor = 'black', value = 'clock').grid(column = 6, row = 3, sticky="news", pady=(0,10))            
+            self.countclock_arc = tk.Radiobutton(
+                self.frame4, text = 'Counterclockwise', fg = 'white', bg = self.parent['bg'],
+                height = 3, width = 20, var = self.clock[ii], 
+                selectcolor = 'black', value = 'countclock').grid(column = 7, row = 3, sticky="news", pady=(0,10))
+            # self.button_edit = tk.Radiobutton(
+            #     self.frame[-1], text = 'Edit', fg = 'white', bg = self.parent['bg'],
+            #     height = 3, width = 20, var = self.draw[ii], 
+            #     selectcolor = 'black', value = 'edit').grid(column = 6,row = 3)
+            self.button_save = tk.Button(
+                frame3, text = 'Save', fg = 'white', bg = self.parent['bg'],
+                height = 3, width = 15, 
+                command = self.save_file).grid(column = 1, row = 3, sticky="news", pady=10)
+            self.button_upload = tk.Button(
+                frame3, text = 'Upload coordinates', fg = 'white', 
+                bg = self.parent['bg'], height = 3, width = 15, 
+                command = self.upload_sa).grid(column = 0, row = 3, sticky="news", pady=10, padx=(10,0))
+            self.button_reset = tk.Button(
+                frame3, text = 'Reset', fg = 'white', 
+                bg = self.parent['bg'], height = 3, width = 15, 
+                command = self.reset).grid(column = 2, row = 3, sticky="news", pady=10)
+            button_main = tk.Button(
+                frame3, text="Done", fg = 'white', 
+                bg = self.parent['bg'], height = 3, width = 15,
+                command = self.check_quit).grid(column = 3, row = 3, sticky="news", pady=10)
+            style = ttk.Style()
+            style.configure(
+                "Treeview", background = 'white', foreground = 'white', 
+                rowheight = 25, fieldbackground = 'white', 
+                font = self.controller.pages_font)
+            style.configure("Treeview.Heading", 
+                            font = self.controller.pages_font)
+            style.map('Treeview', background = [('selected', 'grey')])
+            
+
+            # tree_frame.grid(row = 0, column = 4, columnspan = 3, rowspan = 10)
+            
+            tree_scrollx = tk.Scrollbar(tree_frame, orient = 'horizontal')
+            tree_scrollx.pack(side = tk.BOTTOM, fill = tk.X)
+            tree_scrolly = tk.Scrollbar(tree_frame)
+            tree_scrolly.pack(side = tk.RIGHT, fill = tk.Y)
+            
+            self.tree.append(ttk.Treeview(
+                tree_frame, 
+                yscrollcommand = tree_scrolly.set, 
+                xscrollcommand = tree_scrollx.set))
+            self.tree[-1].pack(fill='both', expand=True)
+            
+            tree_scrollx.config(command = self.tree[-1].xview)
+            tree_scrolly.config(command = self.tree[-1].yview)
+            
+            if self.type[-1] == 'Rotating':
+                self.x_ini, self.y_ini = int(self.width/2), int(self.height/2)
+                self.r = int(np.min([self.x_ini, self.y_ini])/1.5)
+                self.create_circle(self.x_ini, self.y_ini, 4, 
+                                    fill="#DDD", outline="", width=4)
+                self.create_circle(self.x_ini, self.y_ini, self.r, 
+                                    fill="", outline="#DDD", width=4)
+                
+            self.tree[-1]['columns'] = self._class_list[ii]
+            
+            # Format columns
+            self.tree[-1].column("#0", width = 50)
+            for n, cl in enumerate(self._class_list[ii]):
+                self.tree[-1].column(
+                    cl, width = int(
+                        self.controller.pages_font.measure(str(cl)))+20, 
+                    minwidth = 50, anchor = tk.CENTER)
+                    
+            # Headings
+            self.tree[-1].heading("#0", text = "Sample", anchor = tk.CENTER)
+            for cl in self._class_list[ii]:
+                self.tree[-1].heading(cl, text = cl, anchor = tk.CENTER)
+            
+            # for nn in np.arange(N):
+            #     self.tree[-1].insert(
+            #                 parent = '', index = 'end', iid = (nn+1)*1000, text = 'Trial '+str(nn), 
+            #                 values = tuple())
+            
+            self.tree[-1].tag_configure('odd', foreground = 'black', 
+                                        background='#E8E8E8')
+            self.tree[-1].tag_configure('even', foreground = 'black', 
+                                        background='#DFDFDF')
+        
+            # Define double-click on row action
+            self.tree[-1].bind("<Double-1>", self.OnDoubleClick)
+
+            self.frame1.grid(row = 0, column = 0, sticky="nsew")
+            tree_frame.grid(row = 0, column = 1, sticky="nsew", pady=10, padx=10)
+            frame3.grid(row = 1, column = 0, sticky="sew")
+            self.frame4.grid(row = 1, column = 1, sticky="sew")
+            
+            frame3.grid_columnconfigure(tuple(range(4)), weight=1)
+            self.frame4.grid_columnconfigure(tuple(range(4)), weight=1)
+            self.frame[-1].grid_rowconfigure(0, weight=3)
+            self.frame[-1].grid_rowconfigure(1, weight=1)
+            self.frame[-1].grid_columnconfigure(0, weight=2)
+            self.frame[-1].grid_columnconfigure(1, weight=1)
+
+    def draw_dot(self, event):
+        
+        ii = self.notebook.index(self.notebook.select())
+        if self.draw[ii].get() == 'drag':
+            self.selected = self.canvas[ii].find_overlapping(
+                event.x-10, event.y-10, event.x+10, event.y+10)
+            if self.selected:
+                if len(self.selected) > 2:
+                    self.canvas[ii].selected = self.selected[-2]
+                else:
+                    self.canvas[ii].selected = self.selected[-1]
+                self.canvas[ii].startxy = (event.x, event.y)
+            else:
+                self.canvas[ii].selected = None
+
+        if self.draw[ii].get()  == 'draw':
+            # Draw an oval in the given coordinates
+            self.saved = False
+            # Angle of the selected point with respect to the circle center
+            if self.type[ii] == 'Rotating':
+                alpha, x_o, y_o = self.angle_calc(event.x, event.y, 
+                                                  circxy=True)
+                if self.state[ii].get()  == 'state': # Write state coordinates
+                    self.canvas[ii].create_oval(
+                        x_o-3, y_o-3, x_o+3, y_o+3, fill="black", width=0, 
+                        tags=("state"+str(ii)+'-'
+                              + str(len(self.out_data[ii]['State_a']))))
+                    self.out_data[ii]['State_a'].append((
+                        -np.rad2deg(alpha)+360)%360)
+                    self.state[ii].set('action')
+                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
+                        n = int(self.tree[ii].selection()[0]) + 1
+                        if n%2 == 0:
+                            tag = ('even',)
+                        else:
+                            tag = ('odd',)
+                        self.tree[ii].insert(
+                            parent = '', index = 'end', iid = n, text = n+1, 
+                            values = tuple(self.dict2mat(
+                                self.out_data[ii])[n,:].astype(int)), 
+                            tags = tag)
+                        self.tree[ii].selection_set(str(n))
+                    else:
+                        self.tree[ii].insert(
+                            parent = '', index = 'end', iid = 0, text = 1, 
+                            values = tuple(self.dict2mat(
+                                self.out_data[ii])[0,:].astype(int)), 
+                                tags = ('even',))
+                        self.tree[ii].selection_set(str(0))
+                    
+                elif self.state[ii].get()  == 'action':
+                    if self.clock[ii].get()  == 'clock':
+                        start = self.out_data[ii]['State_a'][-1]
+                        end = np.rad2deg(-alpha)
+                    else:
+                        start = self.out_data[ii]['State_a'][-1] - 360
+                        end = 360 - np.rad2deg(alpha)
+                    self.create_circle_arc(
+                        self.x_ini, self.y_ini, self.r, fill = "", 
+                        outline = "red", start = start, end = end, width=2, 
+                        style = tk.ARC, tags = ("action"+str(ii)+'-' + str(len(
+                            self.out_data[ii]['Action_a']))))              
+                    self.out_data[ii]['Action_a'].append((
+                        -np.rad2deg(alpha)+360)%360)
+                    self.state[ii].set('state')
+                    n = int(self.tree[ii].selection()[0])
+                    self.tree[ii].item(
+                        self.tree[ii].get_children()[n], text = n+1, 
+                        values = tuple(self.dict2mat(
+                            self.out_data[ii])[n,:].astype(int)))
+            else:
+                if self.state[ii].get()  == 'state': # Write state coordinates
+                    self.canvas[ii].create_oval(
+                        event.x-3, event.y-3, event.x+3, event.y+3, 
+                        fill="black", width=0, 
+                        tags=("state"+str(ii)+'-'+ str(len(
+                            self.out_data[ii]['State_x']))))
+                    self.out_data[ii]['State_x'].append(event.x)
+                    self.out_data[ii]['State_y'].append(event.y)
+                    self.state[ii].set('action')
+                    if self.tree[ii].selection(): # Update coordinates in corresponding row if exists.
+                        n = int(self.tree[ii].selection()[0]) + 1
+                        if n%2 == 0:
+                            tag = ('even',)
+                        else:
+                            tag = ('odd',)
+                        self.tree[ii].insert(
+                            parent = '', index = 'end', iid = n, text = n+1, 
+                            values = tuple(self.dict2mat(
+                                self.out_data[ii])[n,:].astype(int)), 
+                            tags = tag)
+                        self.tree[ii].selection_set(str(n))
+                    else:
+                        self.tree[ii].insert(
+                            parent = '', index = 'end', iid = 0, text = 1, 
+                            values = tuple(self.dict2mat(
+                                self.out_data[ii])[0,:].astype(int)), 
+                                tags = ('even',))
+                        self.tree[ii].selection_set(str(0))
+                    
+                elif self.state[ii].get()  == 'action':
+                    self.canvas[ii].create_line(
+                        self.out_data[ii]['State_x'][-1], 
+                        self.out_data[ii]['State_y'][-1], event.x, event.y, 
+                        fill="red", arrow=tk.LAST, 
+                        tags=("action"+str(ii)+'-' + str(len(
+                            self.out_data[ii]['Action_x']))))
+                    self.out_data[ii]['Action_x'].append(event.x)
+                    self.out_data[ii]['Action_y'].append(event.y)
+                    self.state[ii].set('state')
+                    n = int(self.tree[ii].selection()[0])
+                    self.tree[ii].item(
+                        self.tree[ii].get_children()[n], text = n+1, 
+                        values = tuple(self.dict2mat(
+                            self.out_data[ii])[n,:].astype(int)))
+        
+    def on_drag(self, event):
+        
+        ii = self.notebook.index(self.notebook.select())
+        if self.draw[ii].get()  == 'drag' and self.canvas[ii].selected:
+            # move the selected item
+            n = int(self.canvas[ii].gettags("current")[0].split('-')[1])
+            
+            if self.type[ii] == 'Rotating':
+                alpha, dx, dy = self.angle_calc(event.x, event.y, circxy=True)
+                _, dx_pr, dy_pr = self.angle_calc(
+                    self.canvas[ii].startxy[0], self.canvas[ii].startxy[1], 
+                    circxy=True)
+                xa = self.r * np.cos(
+                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.x_ini
+                ya = self.r * np.sin(
+                    np.deg2rad(self.out_data[ii]['Action_a'][n])) + self.y_ini
+                if self.canvas[ii].gettags(
+                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
+                    self.canvas[ii].move(
+                        self.canvas[ii].selected, dx-dx_pr, 
+                        dy-dy_pr)
+                    self.out_data[ii]['State_a'][n] = (360-np.rad2deg(alpha))%360
+
+                    if self.clock[ii].get()  == 'clock':
+                        start = self.out_data[ii]['Action_a'][n]
+                        end = np.rad2deg(-alpha)
+                    else:
+                        start = self.out_data[ii]['Action_a'][n] - 360
+                        end = 360 - np.rad2deg(alpha)
+
+                    self.canvas[ii].itemconfigure(
+                        "action"+str(ii)+'-'+str(n), start = start, 
+                        extent = end - start)
+                    
+                elif self.canvas[ii].gettags(
+                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
+                    
+                    self.out_data[ii]['Action_a'][n] = (360-np.rad2deg(alpha))%360
+
+                    if self.clock[ii].get()  == 'clock':
+                        start = self.out_data[ii]['State_a'][n]
+                        end = np.rad2deg(-alpha)
+                    else:
+                        start = self.out_data[ii]['State_a'][n] - 360
+                        end = 360 - np.rad2deg(alpha)
+                    
+                    self.canvas[ii].itemconfigure(
+                        "action"+str(ii)+'-'+str(n), start = start, 
+                        extent = end - start)
+
+            else:
+                # calculate distance moved from last position
+                dx = event.x - self.canvas[ii].startxy[0]
+                dy = event.y - self.canvas[ii].startxy[1]
+                if self.canvas[ii].gettags(
+                        "current")[0].split('-')[0] == 'state'+str(ii): # If state, updates both the state placement and the arrow
+                    self.canvas[ii].move(self.canvas[ii].selected, dx, dy)
+                    self.canvas[ii].coords(
+                        "action"+str(ii)+'-'+str(n), 
+                        (event.x, event.y, self.out_data[ii]['Action_x'][n], 
+                        self.out_data[ii]['Action_y'][n]))
+                    self.out_data[ii]['State_x'][n] = event.x
+                    self.out_data[ii]['State_y'][n] = event.y  
+                elif self.canvas[ii].gettags(
+                        "current")[0].split('-')[0] == 'action'+str(ii): # If action, updates the arrow end of the line position
+                    self.canvas[ii].coords(
+                        self.canvas[ii].gettags("current")[0], 
+                        (self.out_data[ii]['State_x'][n], 
+                         self.out_data[ii]['State_y'][n], event.x, event.y))
+                    self.out_data[ii]['Action_x'][n] = event.x
+                    self.out_data[ii]['Action_y'][n] = event.y
+            # update last position
+            self.canvas[ii].startxy = (event.x, event.y)                
+            self.tree[ii].item(self.tree[ii].get_children()[n], text = n+1, 
+                           values = tuple(self.dict2mat(
+                               self.out_data[ii])[n,:].astype(int)))
+            self.tree[ii].selection_set(str(n))
+
+    def checkered(self, line_distance):
+        
+        ii = self.notebook.index(self.notebook.select()) 
+        # vertical lines at an interval of "line_distance" pixel
+        for x in range(line_distance, self.width, line_distance):
+            self.canvas[ii].create_line(x, 0, x, self.height, fill="#476042")
+        # horizontal lines at an interval of "line_distance" pixel
+        for y in range(line_distance, self.height, line_distance):
+            self.canvas[ii].create_line(0, y, self.width, y, fill="#476042")   
+            
+    def check_quit(self):
+        if not self.saved:
+            response = messagebox.askokcancel(
+                "Exit?", 
+                "Do you want to leave the program without saving?")
+            if response:
+                self.controller.destroy()
+        else:
+            response = messagebox.askokcancel(
+                "Exit?",
+                "Are you sure you are finished?")
+            self.controller.destroy()
+            
+    def save_file_as(self):
+        self.save_path = asksaveasfile(mode='w')
+        self.save_file()
+        
+    def save_file(self):
+        
+        if self.save_path == '':
+            self.save_path = asksaveasfile(defaultextension = '.txt', filetypes = 
+                                      [('Text file', '.txt'), 
+                                       ('CSV file', '.csv'), 
+                                       ('All Files', '*.*')])
+        if self.save_path is not None: # asksaveasfile return `None` if dialog closed with "cancel".
+            # Transform input for output file    
+            data = {}
+            samples = np.max([len(self.out_data[element][
+                list(self.out_data[element].keys())[0]]) 
+                for element in self.out_data])
+            for ii in self.out_data.keys():
+                for c in self.out_data[ii].keys():
+                    a, b = c.split('_')
+                    aux = self.out_data[ii][c]
+                    aux.extend([-1]*(samples - len(self.out_data[ii][c])))
+                    data[a+str(ii)+'_'+b] = aux
+                    
+            filedata = pd.DataFrame(data, columns = data.keys()).to_string()
+            self.save_path.seek(0) # Move to the first row to overwrite it
+            self.save_path.write(filedata)
+            self.save_path.flush() # Save without closing
+            # typically the above line would do. however this is used to ensure that the file is written
+            os.fsync(self.save_path.fileno())
+            self.saved = True
+         
+    def on_return(self, event):
+        ii = self.notebook.index(self.notebook.select())
+        val = self.tree[ii].item(self.treerow)['values']
+        val = [float(i) for i in val]
+        val[int(self.treecol[1:])-1] = float(self.entry.get())
+        self.tree[ii].item(self.treerow, values = val)
+        self.entry.destroy()        
+        self.saved = False
+        
+        if self.type[ii] == 'Rotating':
+            dx, dy = self.coord_calc(np.deg2rad(360 - val[0]))
+            dx_pr, dy_pr = self.coord_calc(np.deg2rad(360 - self.out_data[ii]['State_a'][self.treerow]))            
+            self.out_data[ii]['State_a'][self.treerow] = float(val[0])
+            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx-dx_pr, 
+                dy-dy_pr)
+            
+            self.out_data[ii]['Action_a'][self.treerow] = float(val[1])
+            if self.clock[ii].get()  == 'clock':
+                start = self.out_data[ii]['State_a'][self.treerow]
+                end = self.out_data[ii]['Action_a'][self.treerow]
+            else:
+                start = self.out_data[ii]['State_a'][self.treerow] - 360
+                end = self.out_data[ii]['Action_a'][self.treerow]
+            
+            self.canvas[ii].itemconfigure(
+                "action"+str(ii)+'-'+str(self.treerow), start = start, 
+                extent = end - start)
+        else:
+            # calculate distance moved from last position
+            dx_s = val[0] - self.out_data[ii]['State_x'][self.treerow]
+            dy_s = val[1] - self.out_data[ii]['State_y'][self.treerow]
+
+            self.canvas[ii].move("state"+str(ii)+'-'+str(self.treerow), dx_s, dy_s)
+
+            self.out_data[ii]['State_x'][self.treerow] = val[0]
+            self.out_data[ii]['State_y'][self.treerow] = val[1]
+
+            self.canvas[ii].coords(
+                "action"+str(ii)+'-'+str(self.treerow), 
+                (self.out_data[ii]['State_x'][self.treerow], 
+                  self.out_data[ii]['State_y'][self.treerow], val[2], val[3]))
+            self.out_data[ii]['Action_x'][self.treerow] = val[2]
+            self.out_data[ii]['Action_y'][self.treerow] = val[3]
+            
+    def OnDoubleClick(self, event):
+        
+        """ Executed when a row is double clicked.
+        Opens an entry box to edit a cell and updates the canvas and the 
+        stored data. """
+        
+        ii = self.notebook.index(self.notebook.select())
+        self.treerow = int(self.tree[ii].identify_row(event.y))
+        self.treecol = self.tree[ii].identify_column(event.x)
+        
+        # get column position info
+        x, y, width, height = self.tree[ii].bbox(self.treerow, self.treecol)
+    
+        # y-axis offset
+        pady = height // 2
+        # pady = 0
+        
+        if hasattr(self, 'entry'):
+            self.entry.destroy()
+            
+        self.entry = tk.Entry(self.tree[ii], justify='center') 
+        
+        if int(self.treecol[1:]) > 0:
+            self.entry.insert(
+                0, self.tree[ii].item(self.treerow)['values'][int(str(self.treecol[1:]))-1])
+            # self.entry['selectbackground'] = '#123456'
+            self.entry['exportselection'] = False
+    
+            self.entry.focus_force()
+            self.entry.bind("<Return>", self.on_return)
+            self.entry.bind("<Escape>", lambda *ignore: self.entry.destroy())
+            
+            self.entry.place(x = x, 
+                             y = y + pady, 
+                             anchor = tk.W, width = width)
+
+    def upload_sa(self):
+        
+        ii = self.notebook.index(self.notebook.select())
+        filename = askopenfilename(initialdir = os.getcwd(), 
+                                   title = 'Select a file', 
+                                   defaultextension = '.txt', 
+                                   filetypes = [('Text file', '.txt'), 
+                                                ('CSS file', '.css'), 
+                                                ('All Files', '*.*')])
+        if filename is not None:
+            data = open(filename,'r')
+            read = False
+            self.draw[ii].set('drag')
+            for n, point in enumerate(data):
+                if read: # Not elegant at all, just to omit the header.
+                    i, sx, sy, ax, ay = point.split()
+                    # Draw an oval in the given coordinates
+                    self.canvas[ii].create_oval(
+                        float(sx)-3, float(sy)-3, float(sx)+3, float(sy)+3, 
+                        fill="black", width=0, 
+                        tags=("state-" + str(len(
+                            self.out_data[ii]['State_x']))))
+                    self.canvas[ii].create_line(
+                        float(sx), float(sy), float(ax), float(ay), 
+                        fill="red", arrow=tk.LAST, 
+                        tags=("action"+str(ii)+"-"+ str(len(
+                            self.out_data[ii]['Action_x']))))
+                    self.out_data[ii]['State_x'].append(sx)
+                    self.out_data[ii]['State_y'].append(sy)
+                    self.out_data[ii]['Action_x'].append(ax)
+                    self.out_data[ii]['Action_y'].append(ay)
+                    self.tree.insert(
+                        parent = '', index = 'end', 
+                        iid = len(self.out_data[ii]['Action_x'])-1, 
+                        text = len(self.out_data[ii]['Action_x']), 
+                        values = tuple(self.dict2mat(
+                            self.out_data[ii])
+                            [len(self.out_data[ii]
+                                 ['Action_x'])-1,:].astype(int)))
+                    self.tree.selection_set(str(len(
+                        self.out_data[ii]['Action_x'])-1))
+                else:
+                    read = True
+    
+    def reset(self):
+        
+        ii = self.notebook.index(self.notebook.select())
+        msg = messagebox.askyesnocancel(
+            'Info', 'Are you sure you want to reset the canvas?')
+        if msg:
+            self.canvas[ii].delete(tk.ALL) # Reset canvas
+            self.state[ii].set('state')
+            self.out_data[ii] = {}
+            for key in self._class_list[ii]:
+                self.out_data[ii][key] = []
+                    
+            for record in self.tree[ii].get_children(): #Reset treeview
+                self.tree[ii].delete(record)
+            if self.type[ii] == 'Rotating': 
+                self.create_circle(self.x_ini, self.y_ini, 4, 
+                                   fill="#DDD", outline="", width=4)
+                self.create_circle(self.x_ini, self.y_ini, self.r, 
+                                   fill="", outline="#DDD", width=4)
+    
+    def dict2mat(self, X):
+        
+        max_size = 0
+        for key in X:
+            if len(X[key]) > max_size:
+                max_size = len(X[key])
+
+        result = -np.ones((max_size, len(X)))
+        for k, key in enumerate(X):
+            result[:len(X[key]), k]  = X[key]
+            
+        return result
+    
+    def angle_calc(self, x, y, circxy = False):
+        if (x == self.x_ini) & (y < self.y_ini):
+            alpha = math.pi/2
+        elif (x == self.x_ini) & (y > self.y_ini):
+            alpha = 3*math.pi/2
+        else:
+            alpha = np.arctan((y-self.y_ini)/(x-self.x_ini))
+            alpha += ((x-self.x_ini) < 0) * math.pi
+            alpha += (alpha < 0) * 2*math.pi
+        if circxy:
+            cx, cy = self.coord_calc(alpha)
+            return alpha, cx, cy
+        else:
+            return alpha
+        
+    def coord_calc(self, alpha):
+        cx = self.r * np.cos(alpha) + self.x_ini
+        cy = self.r * np.sin(alpha) + self.y_ini
+        return cx, cy
+
+        
+    def create_circle(self, x, y, r, **kwargs):
+        
+        return self.canvas[self.notebook.index(
+            self.notebook.select())].create_oval(x-r, y-r, x+r, y+r, **kwargs)
+    
+    def create_circle_arc(self, x, y, r, **kwargs):
+        
+        if "start" in kwargs and "end" in kwargs:
+            kwargs["extent"] = kwargs["end"] - kwargs["start"]
+            # if self.clock == 'countclock':
+            #     kwargs["extent"] += (kwargs["extent"] < 0) * 360
+            del kwargs["end"]
+        return self.canvas[self.notebook.index(
             self.notebook.select())].create_arc(x-r, y-r, x+r, y+r, **kwargs)
\ No newline at end of file
diff --git a/aidesign/UserFeedback/plugins/ManualInput.py b/aidesign/UserFeedback/plugins/ManualInput.py
index 5f1c50be..bbd85748 100644
--- a/aidesign/UserFeedback/plugins/ManualInput.py
+++ b/aidesign/UserFeedback/plugins/ManualInput.py
@@ -3,11 +3,17 @@
 import os
 from PIL import Image, ImageTk
 from tkinter import messagebox, ttk
-from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
+from tkinter.filedialog import asksaveasfile
 import numpy as np
 import pandas as pd
 
-
+_PLUGIN_CLASS_NAME = "ManualInput"
+_PLUGIN_CLASS_DESCRIPTION = "Method of user feedback for binary or classification data"
+_PLUGIN_READABLE_NAMES = {"manual":"default","binary":"alias","classification":"alias"}
+_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2,
+                            "required_children": None,}
+_PLUGIN_REQUIRED_SETTINGS = {"class_list":"list"}
+_PLUGIN_OPTIONAL_SETTINGS = {"image_dir":"str"}
 class ManualInput(tk.Frame,UI):
     def __init__(self, parent, controller, config:dict):
         self.parent = parent    
@@ -20,52 +26,75 @@ def __init__(self, parent, controller, config:dict):
         self.assets_path = os.path.join(self.dirpath,'resources','Assets')
 
         self._class_list = None        
-        pixels = 500
+        pixels = 550
         path = os.path.join(self.dirpath,'resources','example_radiography_images')
         self.N = len(os.listdir(path))
         
         self.image_list = []
         for f in os.listdir(path):
             self.image_list.append(
-                ImageTk.PhotoImage(self.expand2square(
+                self.expand2square(
                     Image.open(os.path.join(path, f)), 
-                    (0, 0, 0)).resize((pixels, pixels)))) # (0, 0, 0) is the padding colour
+                    (0, 0, 0)).resize((pixels, pixels))) # (0, 0, 0) is the padding colour
         
-        # Status bar in the lower part of the window
-        status = tk.Label(self, text='Image 1 of '+str(self.N), bd = 1, 
-                          relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
-                          bg = self.parent['bg'])
-        status.grid(row=20, column=0, columnspan=4, pady = 10, 
-                    sticky = tk.W+tk.E)
+        # Frames
+        self.frame1 = tk.Frame(self, bg = self.parent['bg'])
+        frame4 = tk.Frame(self, bg = self.parent['bg'])
+        frame5 = tk.Frame(self, bg = self.parent['bg'])
+        frame6 = tk.Frame(self, bg = self.parent['bg'])
+        
+        # # Status bar in the lower part of the window
+        # self.status = tk.Label(frame6, text='Image 1 of '+str(self.N), bd = 1, 
+        #                   relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
+        #                   bg = self.parent['bg'])
+        # self.status.pack(fill = tk.BOTH, expand = True, padx=10, pady=(0,10))
         
-        # Inital window
-        self.my_label = tk.Label(self, image = self.image_list[0], 
-                                 bg = self.parent['bg'])
-        self.my_label.grid(column = 0, row = 0, rowspan = 10, columnspan = 3)
-    
         # Buttons initialisation
         self.back_img = ImageTk.PhotoImage(Image.open(
             os.path.join(self.assets_path,'back_arrow.png')).resize((150, 50)))
         self.forw_img = ImageTk.PhotoImage(Image.open(
             os.path.join(self.assets_path,'forw_arrow.png')).resize((150, 50)))
         self.button_back = tk.Button(
-            self, image = self.back_img, bg = self.parent['bg'], 
-            state = tk.DISABLED).grid(column = 0,row = 19)
+            frame4, image = self.back_img, bg = self.parent['bg'], 
+            state = tk.DISABLED)
+        self.button_back.grid(column = 0,row = 19, padx=(10,0), pady=10, sticky="news")
         self.button_save = tk.Button(
-            self, text = 'Save', fg = 'white', bg = self.parent['bg'], height = 3, 
-            width = 20, command = self.save_file).grid(column = 1,row = 19)
+            frame4, text = 'Save', fg = 'white', bg = self.parent['bg'], height = 3, 
+            width = 20, command = self.save_file)
+        self.button_save.grid(column = 1,row = 19, sticky="news", pady=10)
         self.button_forw = tk.Button(
-            self, image = self.forw_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(2)).grid(column = 2,row = 19)
-        
-        button_main = tk.Button(
-            self, text="Done", 
+            frame4, image = self.forw_img, bg = self.parent['bg'], 
+            command = lambda: self.forward_back(2))
+        self.button_forw.grid(column = 2,row = 19, sticky="news", pady=10)
+
+        tk.Button(
+            frame5, text="Done", 
             fg = 'white', bg = self.parent['bg'], height = 3, width = 20, 
-            command = self.check_quit).grid(column = 4,row = 19)
+            command = self.check_quit).grid(column = 4,row = 19, sticky="news", pady=10)
         self._parse_config(config)
         self.save_path = ''
         self.saved = True
         
+        # Inital image
+        img = ImageTk.PhotoImage(self.image_list[0])
+        self.my_img = tk.Label(self.frame1, image = img, 
+                                 bg = self.parent['bg'])
+        self.my_img.image = img
+        self.my_img.pack(fill = tk.BOTH, expand = True, padx=(10,0), pady=(10,0))
+        self.my_img.bind("<Configure>", self.resizing)
+        
+        self.frame1.grid(row = 0, column = 0, sticky="nsew")
+        # self.frame3.grid(row = 0, column = 2, sticky="nsew", pady=10, padx=10)
+        frame4.grid(row = 1, column = 0, sticky="sew")
+        frame5.grid(row = 1, column = 1, sticky="sew")
+        frame6.grid(row = 2, column = 0, columnspan =3, sticky="sew")
+
+        frame4.grid_columnconfigure(tuple(range(3)), weight=1)
+        self.grid_rowconfigure(0, weight=3)
+        self.grid_rowconfigure(1, weight=1)
+        self.grid_columnconfigure(tuple(range(3)), weight=1)
+        self.grid_columnconfigure(0, weight=2)
+        
     def _parse_config(self, config):
         self.config = config
         self.class_list(self.config["plugin"]["options"]["class_list"])
@@ -84,15 +113,21 @@ def class_list(self,value):
         :param value: class labels for binary classification
         :type value: list of strings
         """
-        self._class_list = value
+        if isinstance(value[0], list):
+            self._class_list = value[0]
+        else:
+            self._class_list = value
         self.out_data = np.zeros((self.N, len(self._class_list)))
-        self.button_cl = {}
         
+        frame2 = tk.Frame(self, bg = self.parent['bg'])
+        frame2.grid(row = 0, column = 1, sticky="nsew")
+        
+        self.button_cl = {}
         self.var = {}
         for i,cl in enumerate(self._class_list):
             self.var[0,i] = tk.IntVar(value=self.out_data[0,i])
             self.button_cl[cl] = tk.Checkbutton(
-                self, text = cl, fg = 'white', bg = self.parent['bg'], 
+                frame2, text = cl, fg = 'white', bg = self.parent['bg'], 
                 selectcolor = 'black', height = 3, width = 20, 
                 variable = self.var[0,i], 
                 command=(lambda i=i: self.onPress(0,i)))
@@ -108,7 +143,7 @@ def class_list(self,value):
         style.map('Treeview', background = [('selected', 'grey')])
         
         tree_frame = tk.Frame(self)
-        tree_frame.grid(row = 0, column = 5, columnspan = 4, rowspan = 10)
+        tree_frame.grid(row = 0, column = 2, sticky="nsew", pady=10, padx=10)
         
         tree_scrollx = tk.Scrollbar(tree_frame, orient = 'horizontal')
         tree_scrollx.pack(side = tk.BOTTOM, fill = tk.X)
@@ -118,7 +153,7 @@ def class_list(self,value):
         self.tree = ttk.Treeview(tree_frame, 
                                  yscrollcommand = tree_scrolly.set, 
                                  xscrollcommand = tree_scrollx.set)
-        self.tree.pack()
+        self.tree.pack(fill='both', expand=True)
         
         tree_scrollx.config(command = self.tree.xview)
         tree_scrolly.config(command = self.tree.yview)
@@ -126,7 +161,8 @@ def class_list(self,value):
         self.tree['columns'] = self._class_list
         
         # Format columns
-        self.tree.column("#0", width = 50)
+        self.tree.column("#0", width = 80, 
+                minwidth = 50)
         for n, cl in enumerate(self._class_list):
             self.tree.column(
                 cl, width = int(self.controller.pages_font.measure(str(cl)))+20, 
@@ -170,6 +206,30 @@ def expand2square(self, pil_img, background_color):
             result.paste(pil_img, ((height - width) // 2, 0))
             return result
 
+    def resizing(self, event):
+        """ Resizes window to tree view height and buttons width """
+        n = int(self.tree.selection()[0])
+        iw, ih  = self.image_list[n].width, self.image_list[n].height
+        iw = iw() if type(iw) is not int else iw
+        ih = ih() if type(ih) is not int else ih
+
+        mw, mh  = self.frame1.winfo_width()-14, self.frame1.winfo_height()-14 # Frame border correction
+        if (iw != mw) and (ih != mh):
+            if iw>ih:
+                ih = ih*(mw/iw)
+                r = mh/ih if (ih/mh) > 1 else 1
+                iw, ih = mw*r, ih*r
+            else:
+                iw = iw*(mh/ih)
+                r = mw/iw if (iw/mw) > 1 else 1
+                iw, ih = iw*r, mh*r
+            
+            self.image_list[n] = self.image_list[n].resize(
+                (int(iw), int(ih)))
+        img = ImageTk.PhotoImage(self.image_list[n])
+        self.my_img.config(image=img)
+        self.my_img.image = img
+
     def check_quit(self):
         
         if not self.saved:
@@ -183,21 +243,12 @@ def check_quit(self):
                 "Exit?",
                 "Are you sure you are finished?")
             self.controller.destroy()
-    
-    def open_file(self):
-        
-        self.save_path = askopenfile(mode='r+')
-        if self.save_path is not None:
-            t = self.save_path.read()
-            # textentry.delete('0.0', 'end')
-            # textentry.insert('0.0', t)
-            # textentry.focus()
-            
+
     def save_file_as(self):
         
         self.save_path = asksaveasfile(mode='w')
         self.save_file()
-        
+
     def save_file(self):
         
         if self.save_path == '':
@@ -221,28 +272,18 @@ def forward_back(self, image_number):
         
         self.tree.selection_set(str(int(image_number-1)))
         # Print the corresponding image
-        self.my_label.grid_forget()
-        self.my_label = tk.Label(self, image=self.image_list[image_number-1])
-        
+        self.resizing((0,0))
         # Update button commands
-        self.button_forw = tk.Button(
-            self, image = self.forw_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(
-                image_number+1)).grid(column = 2,row = 19)
-        self.button_back = tk.Button(
-            self, image = self.back_img, bg = self.parent['bg'], 
-            command = lambda: self.forward_back(
-                image_number-1)).grid(column = 0,row = 19)
+        self.button_forw.config(image = self.forw_img, bg = self.parent['bg'], 
+                            command = lambda: self.forward_back(image_number+1),
+                            text = '', state = tk.NORMAL)
+        self.button_back.config(image = self.back_img, bg = self.parent['bg'], 
+                            command = lambda: self.forward_back(image_number-1),
+                            text = '', state = tk.NORMAL)
         if image_number == self.N:
-            self.button_forw = tk.Button(
-                self, image = self.forw_img, bg = self.parent['bg'], 
-                state = tk.DISABLED).grid(column = 2,row = 19)
+            self.button_forw.config(state = tk.DISABLED)
         if image_number == 1:
-            self.button_back = tk.Button(
-                self, image = self.back_img, bg = self.parent['bg'], 
-                state = tk.DISABLED).grid(column = 0,row = 19)
-            
-        self.my_label.grid(column=0, row=0, rowspan = 10, columnspan=3)
+            self.button_back.config(state = tk.DISABLED)
         
         # Classes buttons
         # var = {}
@@ -253,20 +294,15 @@ def forward_back(self, image_number):
             # var[i] = tk.IntVar(value = int(self.out_data[image_number-1,i]))
             # I can not make this be selected when going backwards or forward 
             # if it was previously selected.
-            self.button_cl[cl] = tk.Checkbutton(
-                self, text = cl, fg = 'white', bg = self.parent['bg'], 
+            self.button_cl[cl].config(text = cl, fg = 'white', bg = self.parent['bg'], 
                 selectcolor = 'black', height = 3, width = 20, 
                 variable = self.var[image_number-1,i], 
                 command=(lambda i=i: self.onPress(image_number-1,i)))
-            self.button_cl[cl].grid(column = 4,row = i)
 
-        # Status bar    
-        status = tk.Label(
-            self, text='Image ' + str(image_number) + ' of '+str(self.N), 
-            bd = 1, relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
-            bg = self.parent['bg'])
-        status.grid(row=20, column=0, columnspan=4, pady = 10, 
-                    sticky = tk.W+tk.E)
+        # # Status bar    
+        # self.status.config(text='Image ' + str(image_number) + ' of '+str(self.N), 
+        #     bd = 1, relief = tk.SUNKEN, anchor = tk.E, fg = 'white', 
+        #     bg = self.parent['bg'])
             
     def onPress(self, n,i):
         
@@ -282,4 +318,4 @@ def OnDoubleClick(self, event):
         "Moves to the image corresponding to the row clicked on the tree."
         
         item = self.tree.selection()[0]
-        self.forward_back(self.tree.item(item,"text"))
+        self.forward_back(self.tree.item(item,"text"))
\ No newline at end of file
diff --git a/aidesign/__init__.py b/aidesign/__init__.py
index e2b5da56..954d4c59 100644
--- a/aidesign/__init__.py
+++ b/aidesign/__init__.py
@@ -1,7 +1,7 @@
-from .Settings import Settings
-from .utils import AID
-from. import utils
-from .UserFeedback import UserFeedback
-from .Modelling import Modelling
 from .Core import Core
-from .GUI import GUI
\ No newline at end of file
+# from .Settings import Settings
+# from .utils import AID
+# from. import utils
+# from .UserFeedback import UserFeedback
+# from .Modelling import Modelling
+# from .GUI import GUI
\ No newline at end of file
diff --git a/aidesign/utils/AID_core.py b/aidesign/utils/AID_core.py
index b26589a2..08ab7a71 100644
--- a/aidesign/utils/AID_core.py
+++ b/aidesign/utils/AID_core.py
@@ -1,8 +1,7 @@
 # -*- coding: utf-8 -*-
 import tkinter as tk
 from tkinter import font  as tkfont
-from sys import modules as get_module_name
-from .plugins import aidCanvas
+from aidesign.utils.import_helper import import_plugin
 
 class AID(tk.Tk):
     """
@@ -18,8 +17,6 @@ def __init__(self, *args, **kwargs):
                                         weight="bold")
         self.pages_font = tkfont.Font(family='Helvetica', 
                                       size=12)
-        self.current_module_name = get_module_name[__name__]
-
         self.desired_ui_types = []
         self.top_ui_layer = None
         self.startpage_exist = False
@@ -53,11 +50,22 @@ def add_UI_type_to_frames(self,ui_name):
         :param ui_name: name of the UI method being loaded
         :type ui_name: str 
         """
-        self.desired_ui_types.append(getattr(self.current_module_name,ui_name))
-        self.compare_layer_priority(ui_name)
-        if self.available_ui_types[ui_name]["required_children"] != None:
-            for children in self.available_ui_types[ui_name]["required_children"]:
-                self.add_UI_type_to_frames(children)
+        try:
+            plugin = import_plugin(globals(),ui_name)
+        except:
+            from sys import exit
+            print(
+                "Error: User Interface \"{0}\" not recognised. \
+                \nAvailable methods are: \
+                \n  - {1}"\
+                .format(ui_name, ",\n  - ".join(
+                    [i["name"]for i in self._available_ui_types.values()])))
+            exit(1)
+        self._desired_ui_types.append(plugin)
+        self._compare_layer_priority(ui_name)
+        if self._available_ui_types[ui_name]["required_children"] != None:
+            for children in self._available_ui_types[ui_name]["required_children"]:
+                self._add_UI_type_to_frames(children)
         
 
     def plugin_name(self,ui_type):
diff --git a/aidesign/utils/__init__.py b/aidesign/utils/__init__.py
index 17f3d4e4..75b779ca 100644
--- a/aidesign/utils/__init__.py
+++ b/aidesign/utils/__init__.py
@@ -4,6 +4,6 @@
 
 Let's look for a more automatic way of doing this when we have more modules
 """
-from .AID_core import AID
-from .UserInterfaceClass import UI
-from . import import_helper
+# from .AID_core import AID
+# from .UserInterfaceClass import UI
+# from . import import_helper
diff --git a/aidesign/utils/import_helper.py b/aidesign/utils/import_helper.py
index acc416fe..c94df44d 100644
--- a/aidesign/utils/import_helper.py
+++ b/aidesign/utils/import_helper.py
@@ -6,11 +6,19 @@ def import_plugin(script_config, plugin_name):
     plugin_class = getattr(plugin_list, plugin_name)
     return plugin_class
 
+def import_plugin_absolute(script_config,plugin_package,plugin_name):
+    plugin_list = __import__(plugin_package,
+                             script_config,
+                             {},
+                             [plugin_name])
+    plugin_class = getattr(plugin_list, plugin_name)
+    return plugin_class
 
 def import_module(script_config, module_name):
-    module_list = __import__('aidesign.' + module_name,
+    module_list = __import__('aidesign.' + module_name + "." + module_name + "_core",
                              script_config,
                              {},
                              [module_name])
     module_class = getattr(module_list, module_name)
     return module_class
+
diff --git a/aidesign/utils/plugin_helpers.py b/aidesign/utils/plugin_helpers.py
new file mode 100644
index 00000000..2b521435
--- /dev/null
+++ b/aidesign/utils/plugin_helpers.py
@@ -0,0 +1,143 @@
+import ast
+import os     
+class PluginSpecs(ast.NodeVisitor):
+    def __init__(self) -> None:
+        self.module_dirs = {}
+        self.available_plugins = {}
+        self.get_plugin_files()
+        self.get_plugin_specs()
+
+    def get_plugin_files(self, root_dir=None):
+        if root_dir is None:
+            root_dir = os.path.dirname(os.path.realpath(__file__+"/.."))
+
+        for mod_folder in self.get_clean_module_dirs(root_dir):
+            mod_path = os.path.join(root_dir,mod_folder,"plugins")
+            plugins = self.get_clean_module_dirs(mod_path)
+
+            self.module_dirs[mod_folder] = {}
+            self.module_dirs[mod_folder]["root"] = mod_path
+            self.module_dirs[mod_folder]["files"] = [f for f in plugins if f.endswith(".py")]
+
+    def get_clean_module_dirs(self, root_dir:str=None) -> list:
+        excludes = ["__init__.py","__pycache__","resources"]
+        if os.path.exists(root_dir):
+            mod_dirs = os.listdir(root_dir)
+            for ex in excludes:
+                if ex in mod_dirs:
+                    mod_dirs.remove(ex)
+            return mod_dirs
+        else:
+            return []
+
+    def plugins_generator(self):
+        plugin_names = self.module_dirs[self.curr_module]["files"]
+        for plugin in plugin_names:
+            self.curr_plugin = plugin
+            yield os.path.join(self.module_dirs[self.curr_module]["root"],plugin)
+
+    def modules_generator(self):
+        for self.curr_module in self.module_dirs.keys():
+            self.available_plugins[self.curr_module] = {}
+            plugins = self.plugins_generator()
+            for plugin_dir in plugins:
+                yield plugin_dir
+
+    def get_module_package(self):
+        if __package__:
+            parent_package = __package__.split(".")[0]
+        else:
+            parent_package = "aidesign"
+        scipt_filename = self.curr_plugin.replace(".py","")
+        return "{0}.{1}.plugins.{2}".format(parent_package,self.curr_module,scipt_filename)
+
+    def get_plugin_specs(self):
+        self.modules = self.modules_generator()
+        for plugin_dir in self.modules:
+            self.available_plugins[self.curr_module]\
+                                    [self.curr_plugin] \
+                                    = {"_PLUGIN_DIR":plugin_dir,
+                                       "_PLUGIN_PACKAGE":self.get_module_package()
+                                    }
+            with open(plugin_dir, "r") as source:
+                tree = ast.parse(source.read())
+            self.visit(tree)
+
+    def visit_Assign(self, node):
+        if type(node.targets[0]) == ast.Name:
+            if "_PLUGIN" in node.targets[0].id:
+                key = node.targets[0].id
+                val = eval(compile(ast.Expression(node.value),"<ast expression>","eval"))
+                self.available_plugins[self.curr_module][self.curr_plugin][key] = val
+
+    def get_option_specs(self, option:str) -> dict:
+        output = {}
+        for module in self.available_plugins.keys():
+            output[module] = {}
+            for plugin in self.available_plugins[module].keys():
+                if option in self.available_plugins[module][plugin]:
+                    output[module][plugin] = self.available_plugins\
+                                                        [module]\
+                                                        [plugin]\
+                                                        [option]
+        return output
+
+    def find_plugin_by_tag_and_value(self, tag:str, name:str) -> dict:
+        for module in self.available_plugins.keys():
+            for plugin in self.available_plugins[module].keys():
+                if tag in self.available_plugins[module][plugin]:
+                    if name in self.available_plugins[module][plugin][tag]:
+                        return self.available_plugins\
+                                                [module]\
+                                                [plugin]
+        return None
+
+    @property
+    def names(self):
+        return self.get_option_specs("_PLUGIN_READABLE_NAMES")
+
+    @property
+    def class_names(self):
+        return self.get_option_specs("_PLUGIN_CLASS_NAME")
+
+    @property
+    def required_settings(self):
+        return self.get_option_specs("_PLUGIN_REQUIRED_SETTINGS")
+
+    @property
+    def class_descriptions(self):
+        return self.get_option_specs("_PLUGIN_CLASS_DESCRIPTION")
+
+    @property
+    def optional_settings(self):
+        return self.get_option_specs("_PLUGIN_OPTIONAL_SETTINGS")
+
+    @property
+    def available_plugin_names(self):
+        names = self.names
+        output = []
+        for n in names.keys():
+            for plugin in names[n]:
+                for variant in names[n][plugin].keys():
+                    if names[n][plugin][variant] == "default":
+                        output.append(variant)
+        return output
+
+    def find_from_class_name(self, value):
+        return self.find_plugin_by_tag_and_value("_PLUGIN_CLASS_NAME",value)
+
+    def find_from_readable_name(self, value):
+        return self.find_plugin_by_tag_and_value("_PLUGIN_READABLE_NAMES",value)
+
+    def print(self, value):
+        from pprint import PrettyPrinter
+        pp = PrettyPrinter(sort_dicts=False, width=100)
+        pp.pprint(value)
+
+
+# if __name__ == "__main__":
+#     ps = PluginSpecs()
+#     ps.print(ps.get_all_available_plugin_names())
+    # ps.print(ps.names)
+    # ps.print(ps.class_descriptions)
+    # print(list(ps.class_descriptions()['GUI'].values()))
\ No newline at end of file
diff --git a/aidesign/GUI/plugins/MainPage.py b/aidesign/utils/plugins/MainPage.py
similarity index 50%
rename from aidesign/GUI/plugins/MainPage.py
rename to aidesign/utils/plugins/MainPage.py
index d97f92f7..7fa294b7 100644
--- a/aidesign/GUI/plugins/MainPage.py
+++ b/aidesign/utils/plugins/MainPage.py
@@ -1,9 +1,19 @@
 import tkinter as tk
 import os
 from PIL import Image, ImageTk
-from tkinter import messagebox
+
 from tkinter.filedialog import askopenfilename, askdirectory
+from scipy.io import loadmat
+
+from aidesign.utils.plugins.dataLoader import dataLoader
+from aidesign.Settings.Settings_core import Settings
 
+_PLUGIN_CLASS_NAME = "MainPage"
+_PLUGIN_CLASS_DESCRIPTION = "Splash and Menu page used as GUI entry-point"
+_PLUGIN_READABLE_NAMES = {"main":"default","main page":"alias","launch page":"alias"}
+_PLUGIN_MODULE_OPTIONS = {"layer_priority": 1,"required_children": ['aidCanvas', 'pluginCanvas']}
+_PLUGIN_REQUIRED_SETTINGS = {}
+_PLUGIN_OPTIONAL_SETTINGS = {}
 class MainPage(tk.Frame):
 
     def __init__(self, parent, controller, config:dict):
@@ -13,6 +23,8 @@ def __init__(self, parent, controller, config:dict):
         self.controller = controller
         self.controller.title('AI Assisted Framework Design')
         
+        self.bg = parent['bg']
+        
         script_dir = os.path.dirname(__file__)
         self.my_img1 = ImageTk.PhotoImage(
                             Image.open(
@@ -23,7 +35,15 @@ def __init__(self, parent, controller, config:dict):
                                     'AIFRED.png')
                                     ).resize((600, 300))
                                 )
-        self.my_label = tk.Label(self, 
+        
+        self.grid_rowconfigure(tuple(range(3)), weight=1)
+        self.grid_columnconfigure(0, weight=1)
+        
+        frame1 = tk.Frame(self, bg = self.bg)
+        frame2 = tk.Frame(self, bg = self.bg)
+        frame3 = tk.Frame(self, bg = self.bg)
+        
+        self.my_label = tk.Label(frame1, 
                                     image = self.my_img1,
                                     bg = parent['bg'])
 
@@ -34,7 +54,7 @@ def __init__(self, parent, controller, config:dict):
                             pady = 10,
                             sticky = tk.NE)
         
-        my_label = tk.Label(self, 
+        my_label = tk.Label(frame2, 
                                 text = 
                                 'Indicate the data folder and define the pipeline.',
                                 pady= 10,
@@ -45,16 +65,25 @@ def __init__(self, parent, controller, config:dict):
                             row = 11,
                             columnspan = 4)
         
-        tk.Button(self,
-                    text = 'Data folder',
+        tk.Button(frame3,
+                    text = 'Data file',
                     fg = 'white',
                     font = controller.title_font, 
                     bg = parent['bg'],
                     height = 3,
                     width = 20, 
-                    command = self.upload_data,
+                    command = self.upload_data_file,
                     ).grid(column = 0, row = 12)
-        self.controller.Datalabel = tk.Label(self, 
+        tk.Button(frame3,
+                    text = 'Data folder',
+                    fg = 'white',
+                    font = controller.title_font, 
+                    bg = parent['bg'],
+                    height = 3,
+                    width = 20, 
+                    command = self.upload_data_folder,
+                    ).grid(column = 1, row = 12)
+        self.controller.Datalabel = tk.Label(frame3, 
                                 text = 'Incomplete',
                                 pady= 10,
                                 padx= 10,
@@ -64,18 +93,18 @@ def __init__(self, parent, controller, config:dict):
         self.controller.Datalabel.grid(column = 2,
                             row = 12)
         
-        tk.Button(self,
+        tk.Button(frame3,
                     text = 'Interact with canvas',
                     fg = 'white',
                     font = controller.title_font,
                     bg = parent['bg'],
                     height = 3,
                     width = 20, 
-                    command = self.canvas
+                    command = lambda: self.canvas("aidCanvas")
                     ).grid(column = 0, row = 13)
 
-        tk.Button(self,
-                    text = 'Upoad XML file',
+        tk.Button(frame3,
+                    text = 'Upload XML file',
                     fg = 'white',
                     font = controller.title_font, 
                     bg = parent['bg'],
@@ -84,7 +113,7 @@ def __init__(self, parent, controller, config:dict):
                     command = self.upload_xml,
                     ).grid(column = 1, row = 13)
         
-        self.controller.XMLlabel = tk.Label(self, 
+        self.controller.XMLlabel = tk.Label(frame3, 
                                 text = 'Incomplete',
                                 pady= 10,
                                 padx= 10,
@@ -93,8 +122,18 @@ def __init__(self, parent, controller, config:dict):
                                 fg = 'white')
         self.controller.XMLlabel.grid(column = 2,
                             row = 13)
-        
-        self.RunButton = tk.Button(self,
+        self.PluginButton = tk.Button(frame3,
+                    text = 'Modules plugins',
+                    fg = 'white',
+                    font = controller.title_font, 
+                    bg = parent['bg'],
+                    height = 3,
+                    width = 20, 
+                    state = tk.DISABLED,
+                    command = lambda: self.canvas("pluginCanvas"),
+                    )
+        self.PluginButton.grid(column = 0, row = 14)
+        self.RunButton = tk.Button(frame3,
                     text = 'Run Pipeline',
                     fg = 'white',
                     font = controller.title_font, 
@@ -104,46 +143,101 @@ def __init__(self, parent, controller, config:dict):
                     state = tk.DISABLED,
                     command = self.controller.destroy,
                     )
-        self.RunButton.grid(column = 0, row = 14)
+        self.RunButton.grid(column = 1, row = 14)
         
         self.controller.XML = tk.IntVar()
         self.controller.Data = tk.IntVar()
+        self.controller.Plugin = tk.IntVar()
         self.controller.XML.set(False)
         self.controller.Data.set(False)
+        self.controller.Plugin.set(False)
         
         self.controller.XML.trace('w', self.trace_XML)
         self.controller.Data.trace('w', self.trace_Data)
-
+        self.controller.Plugin.trace('w', self.trace_Plugin)
+        
+        frame1.grid(column=0, row=0, sticky="n")
+        frame2.grid(column=0, row=1, sticky="n")
+        frame3.grid(column=0, row=2, sticky="n")
+        
     def trace_XML(self,*args):
+        """ Checks if XML variable has been updated
+        """
         if self.controller.XML.get():
             self.controller.XMLlabel.config(text = 'Done!', fg = 'green')
             if self.controller.Data.get():
-                self.RunButton.config(state = 'normal')
+                self.PluginButton.config(state = 'normal')
 
     def trace_Data(self,*args):
+        """ Checks if Data variable has been updated
+        """
         if self.controller.Data.get():
             self.controller.Datalabel.config(text = 'Done!', fg = 'green')
             if self.controller.XML.get():
+                self.PluginButton.config(state = 'normal')
+            if self.controller.Plugin.get():
                 self.RunButton.config(state = 'normal')
 
-    def canvas(self):
-        self.controller._show_frame("aidCanvas")
-    
+    def trace_Plugin(self,*args):
+        """ Checks if Plugin variable has been updated
+        """
+        if self.controller.Plugin.get() and self.controller.Data.get():
+            self.RunButton.config(state = 'normal')
+
+    def canvas(self,name: str):
+        """ Shows the canvas frame.
+        :param name: string type of name of desired canvas.
+        """
+        self.controller._show_frame(name)
+
     def upload_xml(self):
+        """ Indicates the XML file containng the desired pipeline """
         filename = askopenfilename(initialdir = os.getcwd(), 
                                    title = 'Select a file', 
                                    defaultextension = '.xml', 
                                    filetypes = [('XML file', '.xml'), 
                                                 ('All Files', '*.*')])
-        self.controller._append_to_output("xml_filename",filename)
-        self.controller.XML.set(True)
-        # self.controller.XMLlabel.config(text = 'Done!', fg = 'green')
+        if filename is not None and len(filename) > 0:
+            self.s = Settings()
+            self.s.load_XML(filename)
+            self.isPlug = []
+            self.checkPlugin(self.s.loaded_modules)
+            self.controller._append_to_output("xml_filename",filename)
+            self.controller.XML.set(True)
+            if len(self.isPlug)-2 == sum(self.isPlug): # Considering Init and Out don't have plugins now
+                self.controller.Plugin.set(True)
+
+    def checkPlugin(self, modules: dict):
+        
+        for key, arg in modules.items():
+            if arg['class'] == 'loop':
+                for k, a in arg.items():
+                    if type(a) == dict:
+                        self.checkPlugin({k: a})
+            else:
+                self.isPlug.append(arg['plugin']['plugin_name'] != '')
+
+    def upload_data_file(self):
+        """ Loads a data file containing the data required for the pipeline """
+        filename = askopenfilename(initialdir = os.getcwd(), 
+                                   title = 'Select a file', 
+                                   defaultextension = '.mat', 
+                                   filetypes = [('mat file', '.mat'), 
+                                                ('CSV', '.csv'), 
+                                                ('All Files', '*.*')])
+        
+        if filename is not None and len(filename) > 0:
+            if filename.lower().endswith(('.mat')):
+                data = loadmat(filename)
+                dL = dataLoader(self.controller, data, filename)
+                self.controller.Data = dL.controller.Data
 
-    def upload_data(self):
+    def upload_data_folder(self):
+        """ Stores the directory containing the data that will be later loaded 
+        """
         filename = askdirectory(initialdir = os.getcwd(),
                                     title = 'Select a folder',
                                     mustexist = True)
         self.controller._append_to_output("data_filename",filename)
-        if filename is not None:
+        if filename is not None and len(filename) > 0:
             self.controller.Data.set(True)
-            # self.controller.Datalabel.config(text = 'Done!', fg = 'green')
\ No newline at end of file
diff --git a/aidesign/utils/plugins/__init__.py b/aidesign/utils/plugins/__init__.py
index d8d558a5..d8364cf1 100644
--- a/aidesign/utils/plugins/__init__.py
+++ b/aidesign/utils/plugins/__init__.py
@@ -4,5 +4,4 @@
 
 Let's look for a more automatic way of doing this when we have more modules
 """
-from ..UserInterfaceClass import UI
-from .aidCanvas import aidCanvas
\ No newline at end of file
+from ..UserInterfaceClass import UI
\ No newline at end of file
diff --git a/aidesign/utils/plugins/aidCanvas.py b/aidesign/utils/plugins/aidCanvas.py
index 4c01ee24..20e6c448 100644
--- a/aidesign/utils/plugins/aidCanvas.py
+++ b/aidesign/utils/plugins/aidCanvas.py
@@ -1,3 +1,4 @@
+# Import the required libraries
 import tkinter as tk
 from PIL import Image, ImageTk
 import os
@@ -8,12 +9,19 @@
 
 from aidesign.Settings.Settings_core import Settings
 
+_PLUGIN_CLASS_NAME = "aidCanvas"
+_PLUGIN_CLASS_DESCRIPTION = "Canvas for graphical specification of pipeline modules"
+_PLUGIN_READABLE_NAMES = {"aid_canvas":"default","aidCanvas":"alias","aid":"alias","AID":"alias"}
+_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2,
+                            "required_children": None,}
+_PLUGIN_REQUIRED_SETTINGS = {}
+_PLUGIN_OPTIONAL_SETTINGS = {}
 class aidCanvas(tk.Frame):
 
     """ Creates a frame with a canvas and allows to include different modules
     for display which translate into new modules defined for the framework."""
     
-    def __init__(self, parent, controller, core = None):
+    def __init__(self, parent, controller, config:dict):
         
         " Here we define the main frame displayed upon opening the program."
         " This leads to the different methods to provide feedback."
@@ -21,35 +29,38 @@ def __init__(self, parent, controller, core = None):
         super().__init__(parent, bg = parent['bg'])
         self.bg = parent['bg']
         self.controller = controller
-        self.controller.title('Assisted Design Display')
         
         script_dir = os.path.dirname(__file__)
-        print(os.path.join(
-                script_dir, 
-                'resources',
-                'Assets',
-                'AIDIcon.ico'))
         self.tk.call('wm','iconphoto', self.controller._w, ImageTk.PhotoImage(
-            file = os.path.join(
+            file = os.path.join(os.path.join(
                 script_dir, 
-                'resources',
-                'Assets',
-                'AIDIcon.ico')))
+                'resources', 
+                'Assets', 
+                'AIDIcon.ico'))))
         self.my_img1 = ImageTk.PhotoImage(Image.open(os.path.join(
                 script_dir, 
-                'resources',
-                'Assets',
-                'AIDIcon_name.ico')).resize((250, 200)))
+                'resources', 
+                'Assets', 
+                'AIDIcon_name.png')).resize((250, 200)))
+        
+        self.grid_rowconfigure(tuple(range(2)), weight=1)
+        self.grid_columnconfigure(0, weight=1)
         
-        self.my_label = tk.Label(self, image = self.my_img1, bg = parent['bg'])
+        frame1 = tk.Frame(self, bg = self.bg)
+        frame2 = tk.Frame(self, bg = self.bg)
+        frame3 = tk.Frame(self, bg = self.bg)
+        frame4 = tk.Frame(self, bg = self.bg)
+        
+        self.my_label = tk.Label(frame2, image = self.my_img1, bg = parent['bg'])
         self.my_label.grid(column = 5, row = 0)
         
         # Create canvas
         self.width, self.height = 600, 600
-        self.canvas = tk.Canvas(self, width=self.width, 
+        self.canvas = tk.Canvas(frame1, width=self.width, 
             height=self.height, background="white")
-        self.canvas.grid(row=0, column=0, columnspan=4, rowspan = 10, 
-                         padx = 10, pady = 10)
+        # self.canvas = ResizingCanvas(frame1, width=self.width, 
+        #     height=self.height, background="white")
+        self.canvas.pack(fill = tk.BOTH, expand = True, padx=(10,0), pady=10)
         
         self.canvas.startxy = []
         self.out_data = pd.DataFrame()
@@ -79,60 +90,79 @@ def __init__(self, parent, controller, core = None):
         
         # for m, module in enumerate(modules):
         tk.Button(
-            self, text = 'Data processing', fg = 'white', bg = parent['bg'],
+            frame4, text = 'Data processing', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
             command = lambda: self.add_module('Data Processing', 
                                               self.width/2, 
                                               self.height/2)
-            ).grid(column = 5, row = 1)
+            ).grid(column = 5, row = 1, padx=(0,10), sticky="news")
         tk.Button(
-            self, text = 'Modelling', fg = 'white', bg = parent['bg'],
+            frame4, text = 'Modelling', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
             command = lambda: self.add_module('Modelling', 
                                               self.width/2, 
                                               self.height/2)
-            ).grid(column = 5, row = 2)
+            ).grid(column = 5, row = 2, padx=(0,10), sticky="news")
         tk.Button(
-            self, text = 'Decision making', fg = 'white', bg = parent['bg'],
+            frame4, text = 'Decision making', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
             command = lambda: self.add_module('Decision Making', 
                                               self.width/2, 
                                               self.height/2)
-            ).grid(column = 5, row = 3)
+            ).grid(column = 5, row = 3, padx=(0,10), sticky="news")
         tk.Button(
-            self, text = 'User Feedback', fg = 'white', bg = parent['bg'],
+            frame4, text = 'User Feedback', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
             command = lambda: self.add_module('User Feedback', 
                                               self.width/2, 
                                               self.height/2)
-            ).grid(column = 5, row = 4)
+            ).grid(column = 5, row = 4, padx=(0,10), sticky="news")
         tk.Button(
-            self, text = 'Input data', fg = 'white', bg = parent['bg'],
+            frame4, text = 'Input data', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
             command = lambda: self.add_module('Input Data', 
                                               self.width/2, 
                                               self.height/2)
-            ).grid(column = 5, row = 5)
+            ).grid(column = 5, row = 5, padx=(0,10), sticky="news")
         tk.Button(
-            self, text = 'Delete selection', fg = 'white', bg = parent['bg'],
+            frame4, text = 'Delete selection', fg = 'white', bg = parent['bg'],
             height = 3, width = 25, font = self.controller.pages_font,
-            command = self.delete_sel).grid(column = 5, row = 6)
+            command = self.delete_sel).grid(column = 5, row = 6, sticky="news"
+                                            , padx=(0,10), pady=(0,10))
+        
         tk.Button(
-            self, text = 'Upload', fg = 'white', bg = parent['bg'], 
-            height = 3, width = 20, font = self.controller.pages_font, 
-            command = self.upload).grid(column = 0, row = 10, padx = 10)
+            frame3, text = 'Upload', fg = 'white', bg = parent['bg'], 
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.upload).grid(column = 0, row = 10, sticky="news", 
+                                        padx=(10,0), pady=(0,10))
         tk.Button(
-            self, text = 'Save', fg = 'white', bg = parent['bg'],
-            height = 3, width = 20, font = self.controller.pages_font, 
-            command = self.save_file).grid(column = 1, row = 10)
+            frame3, text = 'Save', fg = 'white', bg = parent['bg'],
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.save_file).grid(column = 1, row = 10, sticky="news", pady=(0,10))
         tk.Button(
-            self, text = 'Reset', fg = 'white', bg = parent['bg'], 
-            height = 3, width = 20, font = self.controller.pages_font, 
-            command = self.reset).grid(column = 2, row = 10)
+            frame3, text = 'Reset', fg = 'white', bg = parent['bg'], 
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.reset).grid(column = 2, row = 10, sticky="news", pady=(0,10))
+        tk.Button(
+            frame3, text = 'Done', fg = 'white', bg = parent['bg'], 
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.check_quit).grid(column = 3, row = 10, sticky="news", pady=(0,10))
         
         self.save_path = ''
         self.saved = True
 
+        frame1.grid(column=0, row=0, rowspan = 2, sticky="nsew")
+        frame2.grid(column=1, row=0, sticky="ne")
+        frame3.grid(column=0, row=2, sticky="swe")
+        frame4.grid(column=1, row=1, sticky="nse")
+        
+        frame3.grid_columnconfigure(tuple(range(4)), weight=1)
+        frame4.grid_rowconfigure(tuple(range(6)), weight=1)
+
+    def class_list(self,value):
+        """ Temporary fix """
+        return value
+
     def on_return_display(self, event):
         """ Defines the type of loop and condition for the indicated loop
         """
@@ -240,7 +270,7 @@ def updateLoops(self):
                 if len(aux) == 2 and aux[0] == 't':
                     self.loops[l]['mod'].append(self.canvas.itemcget(
                         mod, 'text'))
-        # print('Updated loop info:', self.loops)
+        print('Updated loop info:', self.loops)
         
     def select(self, event):
         """ Selects the module at the mouse location. """
@@ -632,7 +662,6 @@ def save_file(self):
                 data = data.drop(columns=col[c], index=col[c])
                 
             loop_modules = np.unique([v for a in self.loops for v in a['mod']])
-
             out_loops = np.zeros_like(self.loops)
             
             values = data.values.astype(bool)
@@ -653,8 +682,7 @@ def save_file(self):
                                   list(mn[values[:,0]]),
                                   list(mn[values[0,:]]),
                                   None,
-                                  [self.canvas.startxy[0], 0, self.connections[0]])      
-            
+                                  [self.canvas.startxy[0], 0, self.connections[0]])
             for i, mnn in enumerate(mn_id):
                 if (i > 1) and mnn:
                     xml_parent = None
@@ -674,7 +702,16 @@ def save_file(self):
                                     out_loops[x] = 1
                                 xml_parent = "loop"+str(x) # parent is the last loop
                                 parent_loops.append("loop"+str(x))
-                                
+
+                    s.append_pipeline_module(self.module_list[i],
+                      mn[i],
+                      "",
+                      {},
+                      list(mn[values[:,i]]),
+                      list(mn[values[i,:]]),
+                      xml_parent,
+                      [self.canvas.startxy[i], i, self.connections[i]])
+                
             s.append_pipeline_module(self.module_list[1], # Out
                       mn[1],
                       "",
@@ -685,7 +722,8 @@ def save_file(self):
                       [self.canvas.startxy[1], 1, self.connections[1]])
             s.write_to_XML()
             self.saved = True
-         
+            self.controller._append_to_output("xml_filename",self.save_path.name)
+
     def upload(self):
         
         filename = askopenfilename(initialdir = os.getcwd(), 
@@ -707,7 +745,6 @@ def upload(self):
             
             # Place the modules
             disp_mod, id_mod = self.place_modules(modules, id_mod, disp_mod)
-    
             connect = list(modout['coordinates'][2].keys())
             for p, parent in enumerate(modout['parents']):
                     parent_id = id_mod[np.where(np.array(disp_mod) == parent)[0][0]]
@@ -726,7 +763,8 @@ def upload(self):
                     self.out_data.iloc[int(parent_id)][1] = 1
                     self.connections[1][
                         int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(1)
-                    
+            self.controller._append_to_output("xml_filename",filename)
+
     def place_modules(self, modules, id_mod, disp_mod):
         # Place the modules
         for key in [key for key, val in modules.items() if type(val) == dict]:
@@ -765,6 +803,7 @@ def place_modules(self, modules, id_mod, disp_mod):
                 self.add_module(key,
                                 modules[key]['coordinates'][0][0],
                                 modules[key]['coordinates'][0][1])
+                self.module_list[-1] = modules[key]['module_type']
                 id_mod.append(modules[key]['coordinates'][1])
                 connect = list(modules[key]['coordinates'][2].keys())
                 
@@ -787,7 +826,6 @@ def place_modules(self, modules, id_mod, disp_mod):
                         self.out_data.iloc[int(parent_id)][int(id_mod[-1])] = 1
                         self.connections[int(id_mod[-1])][
                             int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(id_mod[-1])
-                        print(self.connections)
                     else:
                         self.loops[-1]['mod'].append(key)
                 disp_mod.append(key)
@@ -824,7 +862,40 @@ def reset(self):
             self.loops = []
             self.drawLoop = False
             self.l = 0
-    
+
+    def check_quit(self):
+        
+        if not self.saved:
+            response = messagebox.askokcancel(
+                "Are you sure you want to leave?", 
+                "Do you want to leave the program without saving?")
+            if response:
+                if self.save_path not in [None, '']:
+                    self.controller.XML.set(True)
+                self.controller._show_frame("MainPage")
+        else:
+            if self.save_path not in [None, '']:
+                    self.controller.XML.set(True)
+            self.controller._show_frame("MainPage")
+
+# class ResizingCanvas(tk.Canvas):
+#     def __init__(self,parent,**kwargs):
+#         tk.Canvas.__init__(self,parent,**kwargs)
+#         self.bind("<Configure>", self.on_resize)
+#         self.height = self.winfo_reqheight()
+#         self.width = self.winfo_reqwidth()
+
+#     def on_resize(self,event):
+#         # determine the ratio of old width/height to new width/height
+#         wscale = float(event.width)/self.width
+#         hscale = float(event.height)/self.height
+#         self.width = event.width
+#         self.height = event.height
+#         # resize the canvas 
+#         self.config(width=self.width, height=self.height)
+#         # rescale all the objects tagged with the "all" tag
+#         self.scale("all",0,0,wscale,hscale)
+        
 if __name__ == "__main__":
     app = aidCanvas()
     app.mainloop()
\ No newline at end of file
diff --git a/aidesign/utils/plugins/dataLoader.py b/aidesign/utils/plugins/dataLoader.py
new file mode 100644
index 00000000..7d4e20ad
--- /dev/null
+++ b/aidesign/utils/plugins/dataLoader.py
@@ -0,0 +1,196 @@
+import tkinter as tk
+import os
+from PIL import ImageTk
+from tkinter import ttk
+import numpy as np
+from ttkwidgets import CheckboxTreeview
+from sys import platform
+
+
+class dataLoader():
+    
+    
+    
+    def __init__(self,controller,data,filename):
+        
+        
+        self.controller = controller
+        self.newWindow = tk.Toplevel(self.controller)
+        
+        # Window options
+        self.newWindow.title('Data Importer')
+        script_dir = os.path.dirname(__file__)
+
+        #temp fix - this call does not work on linux
+        if platform != "linux":
+            self.newWindow.iconbitmap(os.path.join(
+                    script_dir, 
+                    'resources', 
+                    'Assets', 
+                    'AIDIcon.ico'))
+        # tk.call('wm','iconphoto', self.newWindow, ImageTk.PhotoImage(
+        #     file = os.path.join(os.path.join(
+        #         script_dir, 
+        #         'resources', 
+        #         'Assets', 
+        #         'AIDIcon.ico'))))
+        self.newWindow.geometry("700x400")
+        self.newWindow.grid_rowconfigure(2, weight=1)
+        self.newWindow.grid_columnconfigure(tuple(range(2)), weight=1)
+        
+        frame1 = tk.Frame(self.newWindow)
+        frame2 = tk.Frame(self.newWindow)
+        tree_frame1 = tk.Frame(self.newWindow, height=300,width=300)
+        tree_frame2 = tk.Frame(self.newWindow, height=300,width=300)
+        frame3 = tk.Frame(self.newWindow)
+        frame4 = tk.Frame(self.newWindow)
+        
+        sizegrip=ttk.Sizegrip(frame4)     # put a sizegrip widget on the southeast corner of the window
+        sizegrip.pack(side = tk.RIGHT, anchor = tk.SE)
+
+        tk.Label(frame1,
+              text ="Select variables to import", 
+              justify=tk.LEFT).pack()
+        tk.Label(frame2,
+              text ="Variables in "+filename, 
+              justify=tk.LEFT).pack()
+        
+        #Treeview 1
+        style = ttk.Style()
+        style.configure(
+            "Treeview",
+            background = 'white', 
+            foreground = 'black', 
+            rowheight = 25, 
+            fieldbackground = 'white', 
+            # font = self.controller.pages_font)
+            )
+        # style.configure("Treeview.Heading", 
+        #                 # font = self.controller.pages_font)
+        #                 )
+        style.map('Treeview', 
+                  background = [('selected', 'grey')])
+        style.layout('cb.Treeview.Row',
+                     [('Treeitem.row', {'sticky': 'nswe'}),
+                      ('Treeitem.image', {'side': 'right', 'sticky': 'e'})])
+        
+        self.tree = []
+        columns = ['Import', 'Name', 'Size', 'Class']
+        self.add_treeview(tree_frame1, data, columns)
+        self.add_treeview(tree_frame2, None, [])
+        tk.Button(
+            frame3, text = 'Finish', fg = 'black', 
+            height = 2, width = 10, #font = self.controller.pages_font, 
+            command = self.check_quit).pack(side = tk.RIGHT, anchor = tk.W)
+
+        tree_frame1.grid(column=0, row=2, sticky="nsew")
+        tree_frame2.grid(column=1, row=2, sticky="nsew")
+        frame1.grid(column=0, row=0, columnspan=2, sticky="nw")
+        frame2.grid(column=0, row=1, columnspan=2, sticky="nw")
+        frame3.grid(column=0, row=3, columnspan=2, sticky="n")
+        frame4.grid(column=0, row=4, columnspan=2, sticky="se")
+
+    def add_treeview(self,frame,data,columns):
+        """ Add a treeview"""
+
+        tree_scrollx = tk.Scrollbar(frame, orient = 'horizontal')
+        tree_scrollx.pack(side = tk.BOTTOM, fill = tk.BOTH)
+        tree_scrolly = tk.Scrollbar(frame)
+        tree_scrolly.pack(side = tk.RIGHT, fill = tk.BOTH)
+        if data is not None:
+            self.tree.append(CheckboxTreeview(
+                frame, 
+                yscrollcommand = tree_scrolly.set, 
+                xscrollcommand = tree_scrollx.set,
+                show=("headings", "tree")))
+        else:
+            self.tree.append(ttk.Treeview(
+                frame, 
+                yscrollcommand = tree_scrolly.set, 
+                xscrollcommand = tree_scrollx.set))
+
+        tree_scrollx.config(command = self.tree[-1].xview)
+        tree_scrolly.config(command = self.tree[-1].yview)
+        # self.tree[-1].pack_propagate(0)
+        
+        self.tree[-1].pack(expand = True, side = tk.LEFT, fill = tk.BOTH)
+        self.fill_treeview(self.tree[-1], data, columns)
+
+    def fill_treeview(self,tree,data,columns):
+        if data is not None:
+            tree['columns'] = columns[1:]
+
+            # Format columns
+            tree.column("#0", width = 50, stretch=tk.NO)
+            for cl in tree['columns']:
+                tree.column(
+                    cl, width = int(
+                        self.controller.pages_font.measure(str(cl)))+30, 
+                    minwidth = 50, anchor = tk.CENTER, stretch=tk.NO)
+
+            # Headings
+            tree.heading("#0", text = columns[0], anchor = tk.CENTER)
+            for cl in tree['columns']:
+                tree.heading(cl, text = cl, anchor = tk.CENTER)
+            
+            variables = [key for key in data.keys() if (key[:1] != '__') and (key[-2:] != '__')]
+            for n, var in enumerate(variables):
+                if n%2 == 0:
+                    tree.insert(parent = '', index = 'end', iid = n, text = '', 
+                                     values = (var, data[var].shape, data[var].dtype), tags = ('even',))
+                else:
+                    tree.insert(parent = '', index = 'end', iid = n, text = '', 
+                                     values = (var, data[var].shape, data[var].dtype), tags = ('odd',))
+
+            # Define double-click on row action
+            if len(self.tree) == 1:
+                tree.bind("<Button-1>", lambda event, d = data: self.OnClick(event, d))
+        else:
+            tree.heading("#0", text = 'No variable selected for preview.', anchor = tk.CENTER)
+
+    def check_quit(self):
+        """ Saves the information and closes the window """
+        self.controller.Data.set(True)
+        self.newWindow.destroy()
+
+    def OnClick(self,event,d):
+        """ On data variable click, updates variable display. """
+        if self.tree[0].identify_column(event.x) == '#1':
+            treerow = self.tree[0].identify_row(event.y)
+            if len(self.tree[0].item(treerow,"values")) > 1:
+                data = d[self.tree[0].item(treerow,"values")[0]]
+                # Reset treeview
+                self.resetTree(self.tree[1])
+                # refill second treeview
+                self.tree[1]['columns'] = list(np.arange(data.shape[1]))
+                # Format columns
+                self.tree[1].column("#0", width = self.controller.pages_font.measure('0'*3)+20, 
+                        minwidth = 0, stretch=tk.NO)
+                for n, cl in enumerate(self.tree[1]['columns']):
+                    self.tree[1].column(
+                        cl, width = int(
+                            self.controller.pages_font.measure(str(cl)*5))+20, 
+                            anchor = tk.CENTER, 
+                            stretch=tk.NO)
+                # Headings
+                self.tree[1].heading("#0", text = '', anchor = tk.CENTER)
+                for cl in self.tree[1]['columns']:
+                    self.tree[1].heading(cl, text = cl, anchor = tk.CENTER)
+                # Data filling
+                for n,row in enumerate(data):
+                    if n%2 == 0:
+                        self.tree[1].insert(parent = '', index = 'end', iid = n, text = n, 
+                                         values = tuple(np.around(row, 3)), tags = ('even',))
+                    else:
+                        self.tree[1].insert(parent = '', index = 'end', iid = n, text = n, 
+                                         values = tuple(np.around(row, 3)), tags = ('odd',))
+                self.tree[1].event_generate("<<ThemeChanged>>")
+            else:
+                self.resetTree(self.tree[1])
+                self.tree[1].column("#0", width = self.controller.pages_font.measure('No variable selected for preview.')+20, 
+                        minwidth = 0, stretch=tk.NO)
+                self.tree[1].heading("#0", text = 'No variable selected for preview.', anchor = tk.CENTER)
+
+    def resetTree(self,tree):
+        tree.delete(*tree.get_children())
+        tree["columns"] = ()
\ No newline at end of file
diff --git a/aidesign/utils/plugins/pluginCanvas.py b/aidesign/utils/plugins/pluginCanvas.py
new file mode 100644
index 00000000..5d651772
--- /dev/null
+++ b/aidesign/utils/plugins/pluginCanvas.py
@@ -0,0 +1,685 @@
+# Import the required libraries
+import tkinter as tk
+from PIL import Image, ImageTk
+import os
+import numpy as np
+import pandas as pd
+from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename
+from tkinter import messagebox
+
+from aidesign.Settings.Settings_core import Settings
+from aidesign.utils.plugin_helpers import PluginSpecs
+
+_PLUGIN_CLASS_NAME = "pluginCanvas"
+_PLUGIN_CLASS_DESCRIPTION = "Canvas for graphical specification of plugins"
+_PLUGIN_READABLE_NAMES = {"plugin_canvas":"default","pluginCanvas":"alias","plugin canvas":"alias"}
+_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2,
+                            "required_children": None}
+_PLUGIN_REQUIRED_SETTINGS = {}
+_PLUGIN_OPTIONAL_SETTINGS = {}
+
+class pluginCanvas(tk.Frame):
+    """ Creates a frame with a canvas and allows to upload a pipeline and 
+    specify the plugins used in each module."""
+    
+    def __init__(self, parent, controller, config:dict):
+        
+        """ Here we define the frame displayed for the plugin specification."""
+        
+        super().__init__(parent, bg = parent['bg'])
+        self.bg = parent['bg']
+        self.controller = controller
+        self.s = Settings()
+        
+        script_dir = os.path.dirname(__file__)
+        self.tk.call('wm','iconphoto', self.controller._w, ImageTk.PhotoImage(
+            file = os.path.join(os.path.join(
+                script_dir, 
+                'resources', 
+                'Assets', 
+                'AIDIcon.ico'))))
+        self.grid_rowconfigure(tuple(range(2)), weight=1)
+        self.grid_columnconfigure(0, weight=1)
+        
+        frame1 = tk.Frame(self, bg = self.bg)
+        self.frame2 = tk.Frame(self, bg = self.bg)
+        frame3 = tk.Frame(self, bg = self.bg)
+        self.frame4 = tk.Frame(self, bg = self.bg)
+        
+        # Create canvas
+        self.width, self.height = 600, 600
+        self.canvas = tk.Canvas(frame1, width=self.width, 
+            height=self.height, background="white")
+        self.canvas.pack(fill = tk.BOTH, expand = True, padx=(10,0), pady=10)
+        
+        self.w, self.h = 100, 50
+        self.cr = 4
+        self.canvas.bind('<Button-1>', self.on_click)
+        self.id_done = [0,1]
+        self.id_mod = [0,1]
+        self.plugin = {}
+        self.allWeHearIs = []
+        
+        self.my_label = tk.Label(self.frame2, 
+                    text = 
+                    '',
+                    pady= 10,
+                    font = self.controller.title_font,
+                    bg = self.bg,
+                    fg = 'white')
+        self.my_label.grid(column = 5,
+                            row = 0, columnspan = 2, padx=10)
+        
+        self.back_img = ImageTk.PhotoImage(Image.open(
+            os.path.join(script_dir,
+                         'resources', 
+                         'Assets', 
+                         'back_arrow.png')).resize((140, 60)))
+        self.forw_img = ImageTk.PhotoImage(Image.open(
+            os.path.join(script_dir,
+                         'resources', 
+                         'Assets',
+                         'forw_arrow.png')).resize((140, 60)))
+    
+        tk.Button(
+            frame3, text = 'Load Pipeline', fg = 'white', bg = parent['bg'], 
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.upload).grid(column = 0, row = 26, sticky="news", 
+                                        padx=(10,0), pady=(0,10))
+        tk.Button(
+            frame3, text = 'Back to main', fg = 'white', bg = parent['bg'], 
+            height = 3, width = 15, font = self.controller.pages_font, 
+            command = self.check_quit).grid(column = 1, row = 26, sticky="news", pady=(0,10))
+        
+        self.save_path = ''
+        self.saved = True
+        frame1.grid(column=0, row=0, sticky="nsew")
+        self.frame2.grid(column=1, row=0, sticky="ne")
+        frame3.grid(column=0, row=1, sticky="swe")
+        self.frame4.grid(column=1, row=1, sticky="sew")
+        
+        frame3.grid_columnconfigure(tuple(range(2)), weight=1)
+        self.frame4.grid_columnconfigure(tuple(range(2)), weight=1)
+        
+    def class_list(self,value):
+        """ Temporary fix """
+        return value
+
+    def on_click(self, event):
+        """ Passes the mouse click coordinates to the select function."""
+        self.select(event.x, event.y)
+        
+    def select(self, x: float, y:float):
+        """ 
+        Selects the module at the mouse location and updates the associated 
+        plugins as well as the colours. 
+        Blue means no plugin has been specified,
+        Orange means the module is selected.
+        Green means the plugin for this module is already set. 
+        :param x: float type of module x coordinate
+        :param y: float type of module y coordinate
+        """
+        self.selected = self.canvas.find_overlapping(
+            x-5, y-5, x+5, y+5)
+        if self.selected:
+            if len(self.selected) > 2:
+                self.canvas.selected = self.selected[-2]
+            else:
+                self.canvas.selected = self.selected[-1]
+        
+        if (self.m in self.plugin.keys()) and\
+                (self.plugin[self.m].get() != 'None') and \
+                (self.m not in self.id_done): # add
+                self.id_done.append(self.m)
+                self.s.append_plugin_to_module(self.plugin[self.m].get(),
+                                               {**self.req_settings, **self.opt_settings},
+                                                 np.array(self.module_names)[self.m == np.array(self.id_mod)][0],
+                                                 True)
+        if self.m in self.id_done and self.m > 1:
+            self.canvas.itemconfig('p'+str(self.m), fill = '#46da63')
+        else:
+            self.canvas.itemconfig('p'+str(self.m), fill = self.bg)
+        
+        if len(self.canvas.gettags(self.canvas.selected)) > 0:
+            if not (len(self.canvas.gettags(self.canvas.selected)[0].split('-')) > 1) and\
+                not (self.canvas.gettags(self.canvas.selected)[0].split('-')[0] == 'loop'):
+                self.m = int(self.canvas.gettags(self.canvas.selected)[0][1:])
+            if self.m > 1:
+                if self.m not in self.id_done and self.m > 1:
+                    self.canvas.itemconfig('p'+str(self.m), fill = '#dbaa21')
+                for widget in self.allWeHearIs:
+                    widget.grid_remove()
+                
+                self.display_buttons()
+                module_number = self.id_mod.index(self.m)
+                if hasattr(self, 'button_forw'):
+                    if module_number == len(self.id_mod)-1:
+                        self.button_forw.config(text = 'Finish', bg = self.bg, 
+                            font = self.controller.pages_font,
+                            fg = 'white', height = 3, width = 15,
+                            command = self.finnish, state = tk.NORMAL, image = '')
+                    else:
+                        pCoord = self.canvas.coords('p'+str(self.id_mod[module_number+1]))
+                        self.button_forw.config(image = self.forw_img, bg = self.bg, 
+                            command = lambda: self.select(
+                                pCoord[0], pCoord[1]), text = '', state = tk.NORMAL)
+                    if module_number < 3:
+                        self.button_back.config(image = self.back_img, bg = self.bg, 
+                            state = tk.DISABLED, text = '')
+                    else:
+                        mCoord = self.canvas.coords('p'+str(self.id_mod[module_number-1]))
+                        self.button_back.config(image = self.back_img, bg = self.bg, 
+                            command = lambda: self.select(
+                                mCoord[0], mCoord[1]), text = '', state = tk.NORMAL)
+                else:
+                    if module_number == len(self.id_mod)-1:
+                        self.button_forw = tk.Button(
+                            self.frame4, text = 'Finish', bg = self.bg, 
+                            font = self.controller.pages_font,
+                            fg = 'white', height = 3, width = 15,
+                            command = self.finnish, state = tk.NORMAL, image = '')
+                    else:
+                        pCoord = self.canvas.coords('p'+str(self.id_mod[module_number+1]))
+                        self.button_forw = tk.Button(
+                            self.frame4, image = self.forw_img, bg = self.bg, 
+                            command = lambda: self.select(
+                                pCoord[0], pCoord[1]), state = tk.NORMAL)
+                    self.button_forw.grid(column = 1,row = 0, sticky="news", pady=(0,10), padx=(0,10))
+                    if module_number < 3:
+                        self.button_back = tk.Button(
+                            self.frame4, image = self.back_img, bg = self.bg, 
+                            state = tk.DISABLED)
+                    else:
+                        mCoord = self.canvas.coords('p'+str(self.id_mod[module_number-1]))
+                        self.button_back = tk.Button(
+                            self.frame4, image = self.back_img, bg = self.bg, 
+                            command = lambda: self.select(
+                                mCoord[0], mCoord[1]), state = tk.NORMAL)
+                    self.button_back.grid(column = 0,row = 0, sticky="news", pady=(0,10))
+            else: # If user clicks on Initialiser or Output
+                self.my_label.config(text = '')
+                for widget in self.allWeHearIs:
+                    widget.grid_remove()
+                if hasattr(self, 'button_forw'):
+                    self.button_back.config(image = self.back_img, bg = self.bg, 
+                            state = tk.DISABLED, text = '')
+                    pCoord = self.canvas.coords('p'+str(self.id_mod[2]))
+                    self.button_forw.config(image = self.forw_img, bg = self.bg, 
+                            command = lambda: self.select(
+                                pCoord[0], pCoord[1]), text = '', state = tk.NORMAL)
+
+    def finnish(self):
+        """ Calls function check_quit.
+        Before that, it checks if the current module plugins have been changed 
+        and, if so, updates their information in the Settings class.
+        """
+        if (self.m in self.plugin.keys()) and\
+                (self.plugin[self.m].get() != 'None') and \
+                (self.m not in self.id_done): # add
+                self.id_done.append(self.m)
+                self.s.append_plugin_to_module(self.plugin[self.m].get(),
+                                               {**self.req_settings, **self.opt_settings},
+                                                 np.array(self.module_names)[self.m == np.array(self.id_mod)][0],
+                                                 True)
+        self.check_quit()
+        
+    def display_buttons(self):
+        """ Updates the displayed radiobuttons and the description windows.
+        It loads the information corresponding to the selected module (self.m)
+        and shows the available plugins and their corresponding descriptions.
+        """
+        module = np.array(self.module_list)[self.m == np.array(self.id_mod)][0]
+        name = self.canvas.itemcget('t'+str(self.m), 'text')
+        self.my_label.config(text = 'Choose a plugin for the '+name+' module')
+        ps = PluginSpecs()
+        plugin_list = list(ps.class_names[module].values())
+        plugin_list.append('Custom')
+        descriptions = list(ps.class_descriptions[module].values())
+        descriptions.append('User specified plugin')
+        if self.m not in self.plugin:
+            self.plugin[self.m] = tk.StringVar()
+            self.plugin[self.m].set(None)
+        self.allWeHearIs = []
+        for p, plug in enumerate(plugin_list):
+            rb = tk.Radiobutton(self.frame2, text = plug, fg = 'white', bg = self.bg,
+                height = 3, width = 20, var = self.plugin[self.m], 
+                selectcolor = 'black', value = plug,
+                font = self.controller.pages_font, command = self.optionsWindow)
+            rb.grid(column = 5+ (p%2 != 0), row = int(p/2)+1)
+            self.CreateToolTip(rb, text = descriptions[p])
+            self.allWeHearIs.append(rb)
+    
+    def optionsWindow(self):
+        """ Function to create a new window displaying the available options 
+        of the selected plugin."""
+        
+        module = np.array(self.module_list)[self.m == np.array(self.id_mod)][0]
+        ps = PluginSpecs()
+        self.opt_settings = ps.optional_settings[module][self.plugin[self.m].get()+'.py']
+        self.req_settings = ps.required_settings[module][self.plugin[self.m].get()+'.py'] 
+        # req_settings = {'arg1': 'int', 'arg2': ['C', 'F']}
+        # opt_settings = {'arg3': 'int', 'arg4': ['C', 'F']}
+        if (len(self.opt_settings) != 0) or (len(self.req_settings) != 0):
+            if hasattr(self, 'newWindow') and (self.newWindow!= None):
+                self.newWindow.destroy()
+            self.newWindow = tk.Toplevel(self.controller)
+            # Window options
+            self.newWindow.title(self.plugin[self.m].get()+' plugin options')
+            script_dir = os.path.dirname(__file__)
+            self.tk.call('wm','iconphoto', self.newWindow, ImageTk.PhotoImage(
+                file = os.path.join(os.path.join(
+                    script_dir, 
+                    'resources', 
+                    'Assets', 
+                    'AIDIcon.ico'))))
+            self.newWindow.geometry("350x400")
+            
+            frame1 = tk.Frame(self.newWindow)
+            frame4 = tk.Frame(self.newWindow)
+        
+            # Print settings
+            tk.Label(frame1,
+                  text ="Please indicate your desired options for the "+self.plugin[self.m].get()+" plugin.\n\
+                      Close the window when you are done.", anchor = tk.N, justify=tk.LEFT).pack(expand = True)
+            self.entry = []
+            # Required
+            r = 1
+            if len(self.req_settings) > 0:
+                frame2 = tk.Frame(self.newWindow)
+                tk.Label(frame2,
+                      text ="Required settings:", anchor = tk.W, justify=tk.LEFT).grid(row=0,column=0 , columnspan=2)
+                self.displaySettings(frame2, self.req_settings)
+                frame2.grid(column=0, row=r, sticky="nswe")
+                r += 1
+            # Optional
+            if len(self.opt_settings) > 0:
+                frame3 = tk.Frame(self.newWindow)
+                tk.Label(frame3,
+                      text ="Optional settings:", anchor = tk.W, justify=tk.LEFT).grid(row=0,column=0, columnspan=2)
+                self.displaySettings(frame3, self.opt_settings)
+                frame3.grid(column=0, row=r, sticky="nswe")
+                r += 1
+                
+            self.entry[0].focus()
+            self.finishButton = tk.Button(
+                frame4, text = 'Finish', command = self.removewindow)
+            self.finishButton.grid(column = 1, row = r+1, sticky="es", pady=(0,10), padx=(0,10))
+            self.finishButton.bind("<Return>", lambda event: self.removewindow())
+            self.newWindow.protocol('WM_DELETE_WINDOW', self.removewindow)
+            
+            frame1.grid(column=0, row=0, sticky="new")
+            frame4.grid(column=0, row=r, sticky="se")
+            self.newWindow.grid_rowconfigure(0, weight=1)
+            self.newWindow.grid_rowconfigure(tuple(range(r+1)), weight=2)
+            self.newWindow.grid_columnconfigure(0, weight=1)
+
+    def displaySettings(self, frame, settings):
+        """ Adds an entry for each input setting. Displays it in the specified row.
+        :param frame: tkinter frame type of frame
+        :param settings: dict type of plugin setting options
+        """
+        r = 1
+        for arg, val in settings.items():
+                tk.Label(frame,
+                  text = arg).grid(row=r,column=0)
+                self.entry.append(EntryWithPlaceholder(frame, val))
+                self.entry[-1].grid(row=r, column=1)
+                self.entry[-1].bind("<Return>", lambda event, a = len(self.entry): self.on_return_entry(a))
+                r += 1
+        frame.grid_rowconfigure(tuple(range(r)), weight=1)
+        frame.grid_columnconfigure(tuple(range(2)), weight=1)
+
+    def removewindow(self):
+        """ Stores settings options and closes window """
+        
+        for e,ent in enumerate(self.entry):
+            if e < len(self.req_settings):
+                self.req_settings[list(self.req_settings.keys())[e]] = ent.get()
+            else:
+                self.opt_settings[list(self.opt_settings.keys())[e-len(self.req_settings)]] = ent.get()
+
+        self.newWindow.destroy()
+        self.newWindow = None
+        self.focus()
+    def on_return_entry(self, r):
+        """ Changes focus to the next available entry. When no more, focuses 
+        on the finish button.
+        : param r: int type of entry id.
+        """
+        if r < len(self.entry):
+            self.entry[r].focus()
+        else:
+            self.finishButton.focus()
+
+    def CreateToolTip(self, widget, text):
+        """ Calls ToolTip to create a window with a widget description. """
+        toolTip = ToolTip(widget)
+        def enter(event):
+            toolTip.showtip(text)
+        def leave(event):
+            toolTip.hidetip()
+        widget.bind('<Enter>', enter)
+        widget.bind('<Leave>', leave)
+        
+    def module_out(self, name):
+        """ Updates the output DataFrame.
+        
+        :param name: name of the model
+        :type name: str
+        """
+        name_list = list(self.out_data.columns)
+        m_num = [n for n in name_list if (n.split('-')[0] == name)]
+        if len(m_num) > 0:
+            name_list.append(name + '-' + str(len(m_num)))
+        else:
+            name_list.append(name)
+        self.canvas.itemconfig('t'+str(self.modules), text = name_list[-1])
+        values = self.out_data.values
+        values = np.vstack((
+                    np.hstack((values, np.zeros((values.shape[0],1)))),
+                    np.zeros((1, values.shape[0]+1))))
+        self.out_data = pd.DataFrame(values, 
+                                     columns = name_list, 
+                                     index = name_list)
+        self.module_names.append(name_list[-1])
+
+    def add_module(self,boxName: str,x: float,y:float,ini = False,out = False):
+        """ Creates a rectangular module with the corresponding text inside.
+        
+        :param boxName: name of the model
+        :type boxName: str
+        :param x: float type of module x coordinate
+        :param y: float type of module y coordinate
+        :param ini: bool type of whether the module corresponds to initialiser.
+        :param out: bool type of whether the module corresponds to output.
+        """
+        if not ini and not out:
+            tag = ('o'+str(self.modules),)
+        else: #Make initialisation and output unmoveable
+            tag = ('n0',)
+        text_w = self.controller.pages_font.measure(boxName+'-00') + 10
+        self.canvas.create_rectangle(
+            x - text_w/2 , 
+            y - self.h/2, 
+            x + text_w/2, 
+            y + self.h/2, 
+            tags = tag + ('p'+str(self.modules),), 
+            fill = self.bg, 
+            width = 3,
+            # activefill = '#dbaa21'
+            )
+        self.canvas.create_text(
+            x, 
+            y, 
+            font = self.controller.pages_font, 
+            text = boxName, 
+            tags = tag + ('t'+str(self.modules),), 
+            fill = '#d0d4d9', 
+            justify = tk.CENTER)
+        
+        if not out:
+            self.canvas.create_oval(
+                x - self.cr, 
+                y + self.h/2 - self.cr, 
+                x + self.cr, 
+                y + self.h/2 + self.cr, 
+                width = 2, 
+                fill = 'black', 
+                tags = tag + ('d'+str(self.modules),))
+            
+        if not ini:
+            self.canvas.create_oval(
+                x - self.cr, 
+                y - self.h/2 - self.cr, 
+                x + self.cr, 
+                y - self.h/2 + self.cr, 
+                width = 2, 
+                fill = 'black', 
+                tags = tag + ('u'+str(self.modules),))
+        
+        if not out and not ini:
+            self.canvas.create_oval(
+                x - text_w/2 - self.cr, 
+                y - self.cr, 
+                x - text_w/2 + self.cr, 
+                y + self.cr, 
+                width = 2, 
+                fill = 'black', 
+                tags = tag + ('l'+str(self.modules),))
+        
+            self.canvas.create_oval(
+                x + text_w/2 - self.cr, 
+                y - self.cr, 
+                x + text_w/2 + self.cr, 
+                y + self.cr, 
+                width = 2, 
+                fill = 'black', 
+                tags = tag + ('r'+str(self.modules),))
+            
+        self.canvas.startxy.append((x, y))
+        self.connections[self.modules] = {}
+        self.module_out(boxName)
+        self.module_list.append(boxName)
+        self.modules += 1
+
+    def upload(self):
+        """ Opens the XML file that was previously uploaded and places the 
+        modules, loops and connections in the canvas."""
+        
+        filename = self.controller.output["xml_filename"]
+        
+        self.reset()
+
+        self.s.load_XML(filename)
+        self.s._print_pretty(self.s.loaded_modules)
+        modules = self.s.loaded_modules
+        modout = modules['output']
+        del modules['Initialiser'], modules['output'] # They are generated when resetting
+        self.disp_mod = ['Initialiser', 'output']
+        self.id_mod = [0, 1]
+        
+        # Place the modules
+        self.place_modules(modules)
+        connect = list(modout['coordinates'][2].keys())
+        for p, parent in enumerate(modout['parents']):
+                parent_id = self.id_mod[np.where(np.array(self.disp_mod) == parent)[0][0]]
+                out, ins = modout['coordinates'][2][connect[p]].split('-')
+                xout, yout, _ , _ = self.canvas.coords(out[0]+str(parent_id))
+                xins, yins, _, _ =self.canvas.coords(ins[0]+str(1))
+                self.canvas.create_line(
+                            xout + self.cr, 
+                            yout + self.cr, 
+                            xins + self.cr, 
+                            yins + self.cr,
+                            fill = "red", 
+                            arrow = tk.LAST, 
+                            tags = ('o'+str(parent_id), 
+                                  'o'+str(1), modout['coordinates'][2][connect[p]]))
+                self.out_data.iloc[int(parent_id)][1] = 1
+                self.connections[1][
+                    int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(1)
+        self.m = self.id_mod[2]
+        x0, y0, x1, y1 = self.canvas.coords('p'+str(self.m))
+        self.select(x0, y0)
+
+    def place_modules(self,modules: dict):
+        """Places the modules in the dictionary in the canvas.
+        :param modules: dict type of modules in the pipeline.
+        """
+        
+        for key in [key for key, val in modules.items() if type(val) == dict]:
+            if modules[key]['class'] == 'loop':
+                # Extracts numbers from string
+                l = int(''.join(map(str, list(filter(str.isdigit, modules[key]['name'])))))
+                x0, y0, x1, y1 = modules[key]['coordinates']
+                self.canvas.create_rectangle(x0, y0, 
+                                             x1, y1, 
+                                             outline = '#4ff07a',
+                                             tag = 'loop-'+str(l))
+                text_w = self.controller.pages_font.measure(modules[key]['type']) + 20
+                self.canvas.create_text(
+                    x0 + text_w/2, 
+                    y0 + 20, 
+                    font = self.controller.pages_font, 
+                    text = modules[key]['type'], 
+                    tags = ('loop-'+str(l), 'type'+'-'+str(l)), 
+                    justify = tk.CENTER)
+                text_w = self.controller.pages_font.measure(modules[key]['condition']) + 20
+                self.canvas.create_text(
+                    x1 - text_w/2, 
+                    y0 + 20, 
+                    font = self.controller.pages_font, 
+                    text = modules[key]['condition'], 
+                    tags = ('loop-'+str(l), 'condition'+'-'+str(l)), 
+                    justify = tk.CENTER)
+                self.loops.append({'type': modules[key]['type'],
+                                   'condition': modules[key]['condition'],
+                                   'mod': [], 
+                                   'coord': (x0, y0, 
+                                             x1, y1)})
+                self.place_modules(modules[key])
+            else:
+                # Display module
+                self.add_module(key,
+                                modules[key]['coordinates'][0][0],
+                                modules[key]['coordinates'][0][1])
+                self.module_list[-1] = modules[key]['module_type']
+                self.id_mod.append(modules[key]['coordinates'][1])
+                connect = list(modules[key]['coordinates'][2].keys())
+                
+                # Connect modules
+                for p, parent in enumerate(modules[key]['parents']):
+                    if not (parent[:4] == 'loop'):
+                        parent_id = self.id_mod[np.where(np.array(self.disp_mod) == parent)[0][0]]
+                        out, ins = modules[key]['coordinates'][2][connect[p]].split('-')
+                        xout, yout, _ , _ = self.canvas.coords(out[0]+str(parent_id))
+                        xins, yins, _, _ =self.canvas.coords(ins[0]+str(self.id_mod[-1]))
+                        self.canvas.create_line(
+                                    xout + self.cr, 
+                                    yout + self.cr, 
+                                    xins + self.cr, 
+                                    yins + self.cr,
+                                    fill = "red", 
+                                    arrow = tk.LAST, 
+                                    tags = ('o'+str(parent_id), 
+                                          'o'+str(self.id_mod[-1]), modules[key]['coordinates'][2][connect[p]]))
+                        self.out_data.iloc[int(parent_id)][int(self.id_mod[-1])] = 1
+                        self.connections[int(self.id_mod[-1])][
+                            int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(self.id_mod[-1])
+                    else:
+                        self.loops[-1]['mod'].append(key)
+                self.disp_mod.append(key)
+
+    def reset(self):
+        """ Resets the canvas and the stored information."""
+        self.canvas.delete(tk.ALL) # Reset canvas
+        
+        if hasattr(self, 'newWindow') and (self.newWindow!= None):
+            self.newWindow.destroy()
+        
+        self.canvas.startxy = []
+        self.out_data = pd.DataFrame()
+        self.connections = {}
+        self.modules = 0
+        self.module_list = []
+        self.module_names = []
+        
+        self.add_module('Initialiser', self.width/2, self.h, ini = True)
+        self.add_module('Output', self.width/2, self.height - self.h, out = True)
+    
+        self.draw = False
+        self.loops = []
+        self.drawLoop = False
+        self.l = 0
+        self.id_done = [0,1]
+        self.plugin = {}
+        for widget in self.allWeHearIs:
+                widget.grid_remove()
+        self.allWeHearIs = []
+        self.my_label.config(text = '')
+
+            
+    def check_quit(self):
+        """Checks if all plugins are specified to reset and go back to the startpage"""
+        if not len(self.id_done) == len(self.id_mod):
+            response = messagebox.askokcancel(
+                "Exit", 
+                "There are some unspecified plugins. Are you sure you want to leave?")
+            if response:
+                self.reset()
+                self.canvas.delete(tk.ALL)
+                self.saved = True
+                self.s.write_to_XML()
+                self.controller._show_frame("MainPage")
+        # TODO: Check if loaded
+        elif len(self.s.loaded_modules) == 0:
+            self.controller._show_frame("MainPage")
+            self.controller.Plugin.set(False)
+        else:
+            self.reset()
+            self.canvas.delete(tk.ALL)
+            self.s.write_to_XML()
+            self.controller.Plugin.set(True)
+            self.controller._show_frame("MainPage")
+
+class ToolTip(object):
+    """ Defines a text window associated to a widget with a description. """
+    
+    def __init__(self, widget):
+        self.widget = widget
+        self.tipwindow = None
+        self.id = None
+        self.x = self.y = 0
+
+    def showtip(self, text):
+        "Display text in tooltip window"
+        self.text = text
+        if self.tipwindow or not self.text:
+            return
+        x, y, cx, cy = self.widget.bbox("insert")
+        x = x + self.widget.winfo_rootx() + 60
+        y = y + cy + self.widget.winfo_rooty() + 50
+        self.tipwindow = tw = tk.Toplevel(self.widget)
+        tw.wm_overrideredirect(1)
+        tw.wm_geometry("+%d+%d" % (x, y))
+        label = tk.Label(tw, text=self.text, justify=tk.LEFT,
+                      background="#d0d4d9", relief=tk.SOLID, borderwidth=1,
+                      font=("Helvetica", "8", "normal"))
+        label.pack(ipadx=1)
+
+    def hidetip(self):
+        tw = self.tipwindow
+        self.tipwindow = None
+        if tw:
+            tw.destroy()
+
+class EntryWithPlaceholder(tk.Entry):
+    """ Defines an entry with a placeholder text displayed if not focused on."""
+    def __init__(self, master=None, placeholder="PLACEHOLDER", color='grey'):
+        super().__init__(master)
+
+        self.placeholder = placeholder
+        self.placeholder_color = color
+        self.default_fg_color = self['fg']
+
+        self.bind("<FocusIn>", self.foc_in)
+        self.bind("<FocusOut>", self.foc_out)
+
+        self.put_placeholder()
+
+    def put_placeholder(self):
+        self.insert(0, self.placeholder)
+        self['fg'] = self.placeholder_color
+
+    def foc_in(self, *args):
+        if self['fg'] == self.placeholder_color:
+            self.delete('0', 'end')
+            self['fg'] = self.default_fg_color
+
+    def foc_out(self, *args):
+        if not self.get():
+            self.put_placeholder()
+            
+if __name__ == "__main__":
+    app = pluginCanvas()
+    app.mainloop()
\ No newline at end of file
diff --git a/aidesign/utils/plugins/resources/Assets/AIFRED.png b/aidesign/utils/plugins/resources/Assets/AIFRED.png
new file mode 100644
index 00000000..fa1b02b2
Binary files /dev/null and b/aidesign/utils/plugins/resources/Assets/AIFRED.png differ
diff --git a/aidesign/GUI/plugins/resources/Hospital.xml b/aidesign/utils/plugins/resources/Assets/Hospital.xml
similarity index 100%
rename from aidesign/GUI/plugins/resources/Hospital.xml
rename to aidesign/utils/plugins/resources/Assets/Hospital.xml
diff --git a/aidesign/GUI/plugins/resources/Assets/UFAIcon.ico b/aidesign/utils/plugins/resources/Assets/UFAIcon.ico
similarity index 100%
rename from aidesign/GUI/plugins/resources/Assets/UFAIcon.ico
rename to aidesign/utils/plugins/resources/Assets/UFAIcon.ico
diff --git a/aidesign/GUI/plugins/resources/Assets/UFAIcon_name.png b/aidesign/utils/plugins/resources/Assets/UFAIcon_name.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/Assets/UFAIcon_name.png
rename to aidesign/utils/plugins/resources/Assets/UFAIcon_name.png
diff --git a/aidesign/GUI/plugins/resources/Assets/back_arrow.png b/aidesign/utils/plugins/resources/Assets/back_arrow.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/Assets/back_arrow.png
rename to aidesign/utils/plugins/resources/Assets/back_arrow.png
diff --git a/aidesign/GUI/plugins/resources/Assets/forw_arrow.png b/aidesign/utils/plugins/resources/Assets/forw_arrow.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/Assets/forw_arrow.png
rename to aidesign/utils/plugins/resources/Assets/forw_arrow.png
diff --git a/aidesign/utils/plugins/resources/Hospital.xml b/aidesign/utils/plugins/resources/Hospital.xml
deleted file mode 100644
index 1d3289ca..00000000
--- a/aidesign/utils/plugins/resources/Hospital.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="Data preprocessing" />
-            </relationships>
-            <coordinates>
-                (314.5, 75.0)
-            </coordinates>
-        </Initialiser>
-        <Data processing name="Data preprocessing">
-            <plugin type="" />
-            <relationships>
-                <parent name="Initialiser" />
-                <child name="Modelling-0" />
-            </relationships>
-            <coordinates>
-                (117, 180)
-            </coordinates>
-        </Data processing>
-        <Modelling name="Modelling-0">
-            <plugin type="" />
-            <relationships>
-                <parent name="Data preprocessing" />
-                <child name="Modelling-1" />
-            </relationships>
-            <coordinates>
-                (117, 273)
-            </coordinates>
-        </Modelling>
-        <loop type="While" condition="Convergence" name="loop0">
-            <relationships>
-                <child name="Modelling-1" />
-                <child name="Input data" />
-                <child name="User Feedback Adaptation" />
-                <child name="Decision making" />
-                <child name="Modelling-1" />
-                <child name="Input data" />
-                <child name="User Feedback Adaptation" />
-                <child name="Decision making" />
-            </relationships>
-            <coordinates>
-                (202.0, 154.0, 585.0, 461.0)
-            </coordinates>
-            <Modelling name="Modelling-1">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Modelling-0" />
-                    <parent name="Input data" />
-                    <parent name="User Feedback Adaptation" />
-                    <parent name="loop0" />
-                    <child name="Decision making" />
-                </relationships>
-                <coordinates>
-                    (257, 326)
-                </coordinates>
-            </Modelling>
-            <Input data name="Input data">
-                <plugin type="" />
-                <relationships>
-                    <parent name="loop0" />
-                    <child name="Modelling-1" />
-                </relationships>
-                <coordinates>
-                    (322, 207)
-                </coordinates>
-            </Input data>
-            <User Feedback Adaptation name="User Feedback Adaptation">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Decision making" />
-                    <parent name="loop0" />
-                    <child name="Modelling-1" />
-                </relationships>
-                <coordinates>
-                    (454, 325)
-                </coordinates>
-            </User Feedback Adaptation>
-            <Decision making name="Decision making">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Modelling-1" />
-                    <parent name="loop0" />
-                    <child name="Output" />
-                    <child name="User Feedback Adaptation" />
-                </relationships>
-                <coordinates>
-                    (354, 419)
-                </coordinates>
-            </Decision making>
-        </loop>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="Decision making" />
-            </relationships>
-            <coordinates>
-                (314.5, 525.0)
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028173_005.png b/aidesign/utils/plugins/resources/example_radiography_images/00028173_005.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028173_005.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028173_005.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028179_000.png b/aidesign/utils/plugins/resources/example_radiography_images/00028179_000.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028179_000.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028179_000.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028198_002.png b/aidesign/utils/plugins/resources/example_radiography_images/00028198_002.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028198_002.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028198_002.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028208_035.png b/aidesign/utils/plugins/resources/example_radiography_images/00028208_035.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028208_035.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028208_035.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028211_021.png b/aidesign/utils/plugins/resources/example_radiography_images/00028211_021.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028211_021.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028211_021.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028253_000.png b/aidesign/utils/plugins/resources/example_radiography_images/00028253_000.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028253_000.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028253_000.png
diff --git a/aidesign/GUI/plugins/resources/example_radiography_images/00028279_000.png b/aidesign/utils/plugins/resources/example_radiography_images/00028279_000.png
similarity index 100%
rename from aidesign/GUI/plugins/resources/example_radiography_images/00028279_000.png
rename to aidesign/utils/plugins/resources/example_radiography_images/00028279_000.png
diff --git a/aidesign/GUI/plugins/startpage.py b/aidesign/utils/plugins/startpage.py
similarity index 95%
rename from aidesign/GUI/plugins/startpage.py
rename to aidesign/utils/plugins/startpage.py
index ac81b759..aaacbd29 100644
--- a/aidesign/GUI/plugins/startpage.py
+++ b/aidesign/utils/plugins/startpage.py
@@ -4,6 +4,12 @@
 from tkinter import messagebox
 from tkinter.filedialog import askopenfilename
 
+_PLUGIN_CLASS_NAME = "StartPage"
+_PLUGIN_CLASS_DESCRIPTION = "Main Page for launching GUI methods"
+_PLUGIN_READABLE_NAMES = {"start":"default","start page":"alias"}
+_PLUGIN_MODULE_OPTIONS = {}
+_PLUGIN_REQUIRED_SETTINGS = {}
+_PLUGIN_OPTIONAL_SETTINGS = {}
 class StartPage(tk.Frame):
 
     def __init__(self, parent, controller):
diff --git a/randomInputData.mat b/randomInputData.mat
new file mode 100644
index 00000000..27255533
Binary files /dev/null and b/randomInputData.mat differ
diff --git a/randomInputData.py b/randomInputData.py
new file mode 100644
index 00000000..d42fa528
--- /dev/null
+++ b/randomInputData.py
@@ -0,0 +1,10 @@
+from scipy.io import savemat
+import numpy as np
+filename = 'randomInputData.mat'
+N = 100
+D = 5
+mydict = {}
+mydict['X'] = np.random.rand(N, D)
+mydict['Y'] = mydict['X'] @ np.random.rand(D, 1) + np.random.normal(0, 0.4, (N, 1))
+
+savemat(filename, mydict)
\ No newline at end of file
diff --git a/run_UI.py b/run_UI.py
index 7ad0792c..3a6af7a9 100644
--- a/run_UI.py
+++ b/run_UI.py
@@ -1,6 +1,11 @@
 import aidesign as ai
+# from setup import SetupPrequisties
 
 core = ai.Core()
-
-# core.load_config_file("./resources/basic_operation.xml")
 core.launch()
+# core.run()
+
+# ['State_a', 'Action_a']
+# ['State_x', 'State_y', 'Action_x', 'Action_y']
+
+# ['a', 'b', 'c', 'd']
diff --git a/setup.py b/setup.py
index 361f89a7..df0e1bd2 100644
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,8 @@ def __init__(self):
             'tk', #package name of tkinter
             'pillow', #package name of PIL
             'pandas', 
-            'sklearn'
+            'sklearn',
+            'ttkwidgets',
             }
         self.python_version = sys.executable
 


Test patch:
diff --git a/test 4.xml b/test 4.xml
deleted file mode 100644
index 32990034..00000000
--- a/test 4.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                [(300.0, 50), 0, {}]
-            </coordinates>
-        </Initialiser>
-        <DataProcessing name="DataProcessing">
-            <plugin type="" />
-            <relationships>
-                <parent name="Initialiser" />
-                <child name="Output" />
-            </relationships>
-            <coordinates>
-                [(300.0, 300.0), 2, {0: 'd0-u2'}]
-            </coordinates>
-        </DataProcessing>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                [(300.0, 550), 1, {2: 'd2-u1'}]
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/test.xml b/test.xml
deleted file mode 100644
index 20060fb0..00000000
--- a/test.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                [(300.0, 50), 0, {}]
-            </coordinates>
-        </Initialiser>
-        <loop type="for" condition="100" name="loop0">
-            <relationships>
-                <child name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                (158.0, 143.0, 443.0, 303.0)
-            </coordinates>
-            <relationships />
-            <UserFeedback name="UserFeedback">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Initialiser" />
-                    <parent name="loop0" />
-                    <child name="Output" />
-                </relationships>
-                <coordinates>
-                    [(274, 218), 2, {0: 'd0-u2'}]
-                </coordinates>
-            </UserFeedback>
-        </loop>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                [(300.0, 550), 1, {2: 'd2-u1'}]
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/test2.xml b/test2.xml
deleted file mode 100644
index 56f43644..00000000
--- a/test2.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                (300.0, 50)
-            </coordinates>
-        </Initialiser>
-        <loop type="For/While" condition="Condition" name="loop0">
-            <relationships>
-                <child name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                (150.0, 197.0, 469.0, 418.0)
-            </coordinates>
-            <relationships />
-            <DataProcessing name="DataProcessing">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Initialiser" />
-                    <parent name="loop0" />
-                    <child name="Output" />
-                </relationships>
-                <coordinates>
-                    (300.0, 300.0)
-                </coordinates>
-            </DataProcessing>
-        </loop>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                (300.0, 550)
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/test3.xml b/test3.xml
deleted file mode 100644
index a494d7a6..00000000
--- a/test3.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="DataProcessing" />
-            </relationships>
-            <coordinates>
-                [(300.0, 50), 0, {}]
-            </coordinates>
-        </Initialiser>
-        <DataProcessing name="DataProcessing">
-            <plugin type="" />
-            <relationships>
-                <parent name="Initialiser" />
-                <child name="Modelling" />
-            </relationships>
-            <coordinates>
-                [(221, 223), 2, {0: 'd0-u2'}]
-            </coordinates>
-        </DataProcessing>
-        <Modelling name="Modelling">
-            <plugin type="" />
-            <relationships>
-                <parent name="DataProcessing" />
-                <child name="Output" />
-            </relationships>
-            <coordinates>
-                [(310, 391), 3, {2: 'd2-u3'}]
-            </coordinates>
-        </Modelling>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="Modelling" />
-            </relationships>
-            <coordinates>
-                [(300.0, 550), 1, {3: 'd3-u1'}]
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/testing.xml b/testing.xml
deleted file mode 100644
index 20060fb0..00000000
--- a/testing.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                [(300.0, 50), 0, {}]
-            </coordinates>
-        </Initialiser>
-        <loop type="for" condition="100" name="loop0">
-            <relationships>
-                <child name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                (158.0, 143.0, 443.0, 303.0)
-            </coordinates>
-            <relationships />
-            <UserFeedback name="UserFeedback">
-                <plugin type="" />
-                <relationships>
-                    <parent name="Initialiser" />
-                    <parent name="loop0" />
-                    <child name="Output" />
-                </relationships>
-                <coordinates>
-                    [(274, 218), 2, {0: 'd0-u2'}]
-                </coordinates>
-            </UserFeedback>
-        </loop>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="UserFeedback" />
-            </relationships>
-            <coordinates>
-                [(300.0, 550), 1, {2: 'd2-u1'}]
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>