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

Issue:
Css plugin selection
# Purpose

Include data file and plugin selection to the `MainPage`. Ability to run pipeline with specified modules and plugins.

# Data file
This new feature allows to indicate a data file and visualize the data within. Once imported, the values stored in each variable can be visualized by clicking on their name.

<p align="center">
<img src="https://user-images.githubusercontent.com/22427519/170203522-39915cf1-fe22-4c10-83a2-ac9980dd30ec.PNG" alt="drawing" width="600"/>
</p>

# Modules plugins
This option can be accessed once the data file or folder is specified and the pipeline is defined. Then it allows importing the pipeline and requires from the user to indicate which plugin each module has. To do so, it reads all the available plugins from the module and displays a list with radio buttons. In order to facilitate understanding what each of them does, putting your mouse over a plugin name displays a description.

![PluginSetupCanvas](https://user-images.githubusercontent.com/22427519/170208388-f9d6daf4-b3c5-4768-ba4c-70d378a1b088.PNG)

Once a plugin is selected, a new window opens to ask for any options the plugin has. These can be either required or optional. You can move from one entry to the next just by clicking enter. At the same time, when an entry is empty, it shows its required information, e.g. `list` or `str`.
<p align="center">
<img src="https://user-images.githubusercontent.com/22427519/170204834-e538f43e-1d2d-4bbd-ada3-e0478564d2a5.PNG" alt="drawing" width="400"/>
</p>

Upon completing this, the user can move to the next module either by clicking on it on the canvas or clicking on the forward arrow. Once a module plugin is indicated, its canvas colour changes to green.

When clicking on `Finish` the plugins and their options are stored in the previouusly defined pipeline XML file.

# Run pipeline
This option can be accessed once the modules plugins are defined. This can be done either interacting with the canvas, as indicated above, or by loading an XML file with all the plugins correctly defined.

# Window resizing
All windows have been modified to work on resizing and dynamically resizing the objects within . To do so, all windows have been devided into several frames that adjust to the window size


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 8e61544f..622ed851 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,289 @@
-# AI assisted design
+![AIFRED](https://user-images.githubusercontent.com/22427519/168077112-840bc213-3804-45de-9174-928a203df555.png)
 
-This framework aims to provide the means to easily build a fully operational Bayesian machine learning pipeline for different domains. 
-This allows to adapt the machine learning algotithms to the task requirements and provide the output that best adapts to the problem needs.
-Furthermore, its modular nature provides a way to intuitively combine certain modules for specific problems.
+This framework aims to provide the means to easily build a fully operational Machine Learning pipeline across different domains. 
+This allows to adapt the machine learning algorithms to the task requirements and provide the output that best adapts to the problem needs.
+Furthermore, its modular nature provides a way to intuitively combine modules for specific problems.
+
+The `Core` class in script `AI_design_core.py` inherits from the `Settings`class, loading the modules declared in the XML file, dynamically importing, instantiating and executing them. 
+
+## Launching Core
+
+Simple launchers were added to `Core` for easy execution. To load and execute core using the Initialisation GUI, run the following:
+
+```python
+import aidesign as ai
+
+core = ai.Core()
+core.launch_canvas()
+core.run()
+```
+
+or to execute the pipeline from an existing config file, use: 
+
+```python
+import aidesign as ai
+
+core = ai.Core()
+core.load_config_file("<path_to_config_file>)
+core.run()
+```
+
+# Initialisation
+
+## Pipeline definition via canvas interaction
+
+The AID module allows to define a pipeline and the relations within by drawing a flowchart on a canvas. This canvas always starts with an `initialiser` module and an `output` module and allows to define any number of modules between these two. To do so, the user needs to define the modules and the relations between them.
+
+![PipelineLoop](https://user-images.githubusercontent.com/22427519/168080733-4c88d004-9ca6-486e-bd8d-cc7050e93515.PNG)
+
+### Modules
+At this moment, there are 7 possible modules for AID. `initialiser` and `output` are compulsory for the pipeline, the rest of them can be freely placed in the canvas. These are:
+ - `Data processing`.
+ - `Modelling`.
+ - `Decision making`.
+ - `User Feedback Adaptation`.
+ - `Input data`.
+
+If you click on a module and drag it you can modify its position on the canvas.
+Finally, they can be deleted by clicking on the desired module and then clicking on the `Delete selection` button.
+
+### Module joining
+Each module object has a number of circles that can be used to join two modules. The initially clicked circle will be identified as the parent and the second one as the child (the output of the father is fed to the input of the child). There can be only one connection from each circle. As of this version, if you need to edit an existing connection you need to delete one of the connected modules.
+
+### Loops
+If you click on the canvas and drag, you can draw a rectangle that defines which modules are inside the loop. Upon releasing the button you are requested to input what type of loop you want and what condition should be fulfilled to end the loop.
+
+### Editing
+You can edit the module's displayed name and the loop's conditions by double-clicking on them and making any desired changes.
+
+### Reset
+Upon clicking the reset button, all objects in the canvas are deleted except for the default modules (`initialiser` and `output`).
+
+
+## Pipeline uploading via XML file
+
+The pipeline can also be defined uploading an existing XML file. The structure of the XML file is described in the Back-end section.
 
 ## UFA
 
 The User Feedback Adaptation module provides the means to easily interact with the machine learning model to give feedback.
-It allows to provide feedback for different domains, adapting the user's input to the corresponding input required by the model. 
-At this stage there are 2 main feedback options:
-- **Manual input**: Requires the user to indicate to which class the specified data corresponds to. 
+It allows the user to provide feedback for different domains, adapting the user's input to the corresponding input required by the model. 
+At this stage there are 2 main feedback options.
+
+### `Manual input`
+Requires the user to indicate to which class the specified data corresponds to. 
 In the current example, the model needs to classify images and the model requires expert knowledge for specific images.
 The user needs to indicate which classes correspond to the image and save the results to send them to the model.
-- **Interact with canvas**: Requires the user to give feedback to state-action pairs.
+
+### `Interact with canvas`
+Requires the user to give feedback to state-action pairs.
 It opens a tab for each interactable object in the model and either requires adding new state-action samples or to modify the existing ones. 
-In the current example, the model has two interactable objects that require feedback in two forms: (1) an _angle_ for the state and for the action or (2) a tuple of _cartesian coordinates_ for the state and for the action.
+In the current example, the model has two interactable objects that require feedback in two forms: (1) an _angle_ for the state and for the action or (2) a tuple of _Cartesian coordinates_ for the state and for the action. It has been adapted to be able to give feedback to any number of objects. These, at the same time, can be either `sliding` or `rotating` objects. Specifically, `sliding` refers to objects that need Cartesian feedback in a two-dimensional space, while `rotating` refers to objects that require an angle. In order to give feedback, you can choose to either move the corresponding state-action pairs on the canvas or directly edit the tree view display. This last option results in an automatic update on the canvas of the state-action location.
+
+# Back-end
+
+## Filetree structure and modularisation
+To support modularisation and maintaining the modules as plugins, they need to be independent of the rest of the file structure. 
+Each module has:
+    - A root folder carrying its name (e.g. `GUI`)
+    - A core script which calls the plugins (e.g. `GUI_core.py`)
+    - An Abstract Class which enforces plugin structure (e.g. `UserInterfaceClass.py`)
+    - A folder containing all plugins relevant to that module
+
+```bash
+.
+├── aidesign
+│   ├── Core
+│   │   ├── AI_design_core.py
+│   │   └── __init__.py
+│   ├── GUI
+│   │   ├── plugins
+│   │   │   ├── __init__.py
+│   │   │   ├── resources
+│   │   ├── __init__.py
+│   │   ├── GUI_core.py
+│   ├── Modelling
+│   │   ├── plugins
+│   │   │   └── regression.py
+│   │   ├── __init__.py
+│   │   └── Modelling_core.py
+│   ├── Settings
+│   │   ├── resources
+│   │   │   ├── example_config.xml
+│   │   │   └── Hospital.xml
+│   │   ├── __init__.py
+│   │   └── Settings_core.py
+│   ├── UserFeedback
+│   │   ├── plugins
+│   │   │   ├── resources
+│   │   │   │   └── example_radiography_images
+│   │   │   ├── __init__.py
+│   │   │   ├── CanvasInput.py
+│   │   │   └── ManualInput.py
+│   │   ├── __init__.py
+│   │   ├── User_Feedback_core.py
+│   │   └── User_Feedback_template.py
+│   ├── utils
+│   │   ├── plugins
+│   │   │   ├── resources
+│   │   │   │   └── Hospital.xml
+│   │   │   ├── __init__.py
+│   │   │   └── aidCanvas.py
+│   │   ├── __init__.py
+│   │   ├── AID_core.py
+│   │   ├── import_helper.py
+│   │   └── UserInterfaceClass.py
+│   └── __init__.py
+├── README.md
+├── run_UI.py
+└── setup.py
+```
+## XML tags
+
+All tags must currently be nested inside the `Settings` tag (This may be removed later):
+```XML
+<Settings>
+    ...
+</Settings>
+```
+
+## Pipeline Definition
+
+The pipeline structure is defined between the `pipeline` tags:
+```XML
+<pipeline>
+    ...
+</pipeline>
+```
+
+### Initialise
+The `Initialise` tag is the dedicated entry point to the pipeline. No other entry points can be declared. 
+
+Current options:
+ - `name`: attribute for user defined name
+ - `initial_data`: element for declaring directory for initial data
+ - `goal file`: element where the objective for the pipeline is declared (consider renaming)
+ - `relationships`: User defined names of modules this one is connected to
+```XML
+<Initialise name="Init">
+        <initial_data file="../resources/intial_data_test.csv" />
+        <goal file="../resources/design_goals_test.csv" />&gt;
+        <relationships>
+            <child name="My First GUI Module" />
+        </relationships>
+</Initialise>
+```
+
+### Loops
+Loop tags are used to iterate over a given set of modules until a condition is met. Loops can be nested and named.  See `example_config.xml` for full example.
+Current options:
+ - `type`: what variety of loop will this be: `for`, `while`, `manual`(user defined stopping condition on-the-fly)
+ - `condition`: Termination condition for the loop. I'm not sure how to deal with the criteria for `while` loops
+ - `name`: User defined name for loop 
+```XML
+<loop type="for" condition="10" name="For Loop 1">
+    ...
+</loop>
+```
+
+### Modules
+Modules are declared by tags matching their names, e.g. the GUI module is loaded with the `GUI` tag:
+
+Required:
+ - `name`: Unique user defined name for module, so can be referenced later
+ - `plugin`: The type of plugin to be loaded into the module, along with associated options.
+ - `relationships`: User-defined names of the `parent` modules which this module receives data from and `child` modules that this module passes data to. Example:
+```XML
+     <GUI name="My First GUI Module">
+         <plugin type="manual">
+             <class_list>
+                Atelectasis
+                Cardiomelagy
+                Effusion
+                Infiltration
+                Mass
+                Nodule
+                Pneumonia
+                Pneumothorax
+            </class_list>
+        </plugin>
+        <relationships>
+            <parent name="Init" />
+            <child name="Data Processing 1" />
+        </relationships>
+    </GUI>
+```
+
+### Output 
+Dedicated exit point for the pipeline, where the save file is declared along with the data to be saved
+
+```XML
+<Output>
+    <out_data>
+        all
+    </out_data>
+    <save_to file="../resources/output_test.csv" />
+    <relationships>
+        <parent name="My Second GUI Module" />
+    </relationships>
+</Output>
+```
+
+## Data Definition
+The structure of the data which will be passed through the pipeline. Currently, limited placeholder tags are declared until we have a better idea of what we want here. The structure of the data must be declared between the `datastructure` tags: 
+
+```XML
+<datastructure name="user_defined_name">
+    ...
+</datastructure>
+```
+
+```XML
+<datastructure name="datatest">
+        <data_fields>
+            State_0
+            State_1
+            Action
+            Reward
+            Termination
+            Goal
+        </data_fields>
+    </datastructure>
+```
+
+### Writing Data
+Two methods are given to add data to the XML file. One for modules (`append_pipeline_module_to_file`) and one for data structures (`append_data_structure_field_to_file`).
+
+## Plugin Option Loader
+
+In script `utils/plugin_helpers.py`, class `PluginSpecs` scrapes options inside plugin scripts to populate a dict of plugins for each module. Additionally, other plugin options are defined in-script with the form: `_PLUGIN_<name of option>`, for example:
+
+```python
+_PLUGIN_CLASS_NAME = "ManualInput"
+_PLUGIN_CLASS_DESCRIPTION = "Method of user feedback for binary or classification data"
+_PLUGIN_READABLE_NAMES = {"default":"manual","aliases":["binary,classification"]}
+_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2,
+                            "required_children": None,}
+_PLUGIN_REQUIRED_SETTINGS = {"class_list":"list","image_dir":"str"}
+_PLUGIN_OPTIONAL_SETTINGS = {}
+```
+
+`PluginSpecs` defines some convenience methods for quickly grabbing all module options, including:
+
+```python
+def names(self):
+    return self.get_option_specs("_PLUGIN_READABLE_NAMES")
+
+def class_names(self):
+    return self.get_option_specs("_PLUGIN_CLASS_NAME")
+
+def class_descriptions(self):
+        return self.get_option_specs("_PLUGIN_CLASS_DESCRIPTION")
+
+def required_settings(self):
+    return self.get_option_specs("_PLUGIN_REQUIRED_SETTINGS")
+```
 
 # Contact
 - Chris McGreavy, chris.mcgreavy@aalto.fi
-- Carlos Sevilla-Salcedo, carlos.sevillasalcedo@aalto.fi
\ No newline at end of file
+- Carlos Sevilla-Salcedo, carlos.sevillasalcedo@aalto.fi
diff --git a/aidesign/GUI/GUI_core.py b/aidesign/GUI/GUI_core.py
index 788f1ab1..947c01cd 100644
--- a/aidesign/GUI/GUI_core.py
+++ b/aidesign/GUI/GUI_core.py
@@ -51,7 +51,6 @@ def _add_UI_type_to_frames(self, ui_specs):
         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_specs)
         if ui_specs["_PLUGIN_MODULE_OPTIONS"]["required_children"] != None:
@@ -72,13 +71,17 @@ def set_plugin_name(self, ui_type: list):
             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. \
-                    \nAvailable methods are: \
-                    \n  - {1}"\
-                    .format(ui, ",\n  - ".join(
+                    "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)
 
diff --git a/aidesign/UserFeedback/UserFeedback_core.py b/aidesign/UserFeedback/UserFeedback_core.py
index 1a3ceb31..1b893a02 100644
--- a/aidesign/UserFeedback/UserFeedback_core.py
+++ b/aidesign/UserFeedback/UserFeedback_core.py
@@ -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/plugins/CanvasInput.py b/aidesign/UserFeedback/plugins/CanvasInput.py
index 78384f3e..585d5a52 100644
--- a/aidesign/UserFeedback/plugins/CanvasInput.py
+++ b/aidesign/UserFeedback/plugins/CanvasInput.py
@@ -23,8 +23,7 @@ def __init__(self, parent, controller, config:dict):
         
         self.controller.title('Canvas Input')
         self.notebook = ttk.Notebook(self)
-        self.notebook.grid(row=0, column=0, columnspan=7, 
-                           rowspan = 12, pady = 15)
+        self.notebook.pack(fill = tk.BOTH, expand = True)
         self.frame = []
         self.tree = []
         self.canvas = []
@@ -35,7 +34,9 @@ def __init__(self, parent, controller, config:dict):
         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"])
@@ -63,7 +64,7 @@ def class_list(self, value):
 
         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)
+            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())
@@ -74,14 +75,19 @@ def class_list(self, value):
             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.frame[-1], width=self.width, 
+                self.frame1, width=self.width, 
                 height=self.height, background="white"))
-            self.canvas[-1].grid(row=0, column=0, columnspan=4, rowspan = 2)
+            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)
@@ -93,41 +99,41 @@ def class_list(self, value):
             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'],
+                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)
+                selectcolor = 'black', value = 'draw').grid(column = 4, row = 3, sticky="news", pady=(0,10))
             self.button_drag = tk.Radiobutton(
-                self.frame[-1], text = 'Move', fg = 'white', bg = self.parent['bg'],
+                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)
+                selectcolor = 'black', value = 'drag').grid(column = 5, row = 3, sticky="news", pady=(0,10))
             self.clock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Clockwise', fg = 'white', bg = self.parent['bg'],
+                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)            
+                selectcolor = 'black', value = 'clock').grid(column = 6, row = 3, sticky="news", pady=(0,10))            
             self.countclock_arc = tk.Radiobutton(
-                self.frame[-1], text = 'Counterclockwise', fg = 'white', bg = self.parent['bg'],
+                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)
+                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(
-                self.frame[-1], text = 'Save', fg = 'white', bg = self.parent['bg'],
-                height = 3, width = 20, 
-                command = self.save_file).grid(column = 1, row = 3)
+                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(
-                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)
+                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(
-                self.frame[-1], text = 'Reset', fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20, 
-                command = self.reset).grid(column = 2, row = 3)
+                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(
-                self.frame[-1], text="Done", fg = 'white', 
-                bg = self.parent['bg'], height = 3, width = 20,
-                command = self.check_quit).grid(column = 3, row = 3)
+                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', 
@@ -138,8 +144,7 @@ def class_list(self, value):
             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_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)
@@ -150,7 +155,7 @@ def class_list(self, value):
                 tree_frame, 
                 yscrollcommand = tree_scrolly.set, 
                 xscrollcommand = tree_scrollx.set))
-            self.tree[-1].pack()
+            self.tree[-1].pack(fill='both', expand=True)
             
             tree_scrollx.config(command = self.tree[-1].xview)
             tree_scrolly.config(command = self.tree[-1].yview)
@@ -191,6 +196,18 @@ def class_list(self, value):
             # 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())
diff --git a/aidesign/UserFeedback/plugins/ManualInput.py b/aidesign/UserFeedback/plugins/ManualInput.py
index 0f298bf0..bbd85748 100644
--- a/aidesign/UserFeedback/plugins/ManualInput.py
+++ b/aidesign/UserFeedback/plugins/ManualInput.py
@@ -3,7 +3,7 @@
 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
 
@@ -12,8 +12,8 @@
 _PLUGIN_READABLE_NAMES = {"manual":"default","binary":"alias","classification":"alias"}
 _PLUGIN_MODULE_OPTIONS = {"layer_priority": 2,
                             "required_children": None,}
-_PLUGIN_REQUIRED_SETTINGS = {"class_list":"list","image_dir":"str"}
-_PLUGIN_OPTIONAL_SETTINGS = {}
+_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    
@@ -26,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)
+            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)
 
-        button_main = tk.Button(
-            self, text="Done", 
+        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"])
@@ -90,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)))
@@ -114,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)
@@ -124,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)
@@ -132,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, 
@@ -176,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:
@@ -189,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 == '':
@@ -227,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 = {}
@@ -259,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):
         
@@ -288,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/utils/plugins/MainPage.py b/aidesign/utils/plugins/MainPage.py
index 47e323c8..7fa294b7 100644
--- a/aidesign/utils/plugins/MainPage.py
+++ b/aidesign/utils/plugins/MainPage.py
@@ -6,6 +6,7 @@
 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"
@@ -174,11 +175,13 @@ def trace_Data(self,*args):
             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 trace_Plugin(self,*args):
         """ Checks if Plugin variable has been updated
         """
-        if self.controller.Plugin.get():
+        if self.controller.Plugin.get() and self.controller.Data.get():
             self.RunButton.config(state = 'normal')
 
     def canvas(self,name: str):
@@ -195,8 +198,24 @@ def upload_xml(self):
                                    filetypes = [('XML file', '.xml'), 
                                                 ('All Files', '*.*')])
         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 """
diff --git a/aidesign/utils/plugins/pluginCanvas.py b/aidesign/utils/plugins/pluginCanvas.py
index f4d79a4d..5d651772 100644
--- a/aidesign/utils/plugins/pluginCanvas.py
+++ b/aidesign/utils/plugins/pluginCanvas.py
@@ -68,7 +68,7 @@ def __init__(self, parent, controller, config:dict):
                     bg = self.bg,
                     fg = 'white')
         self.my_label.grid(column = 5,
-                            row = 0, columnspan = 2)
+                            row = 0, columnspan = 2, padx=10)
         
         self.back_img = ImageTk.PhotoImage(Image.open(
             os.path.join(script_dir,
@@ -131,9 +131,8 @@ def select(self, x: float, y:float):
                 (self.plugin[self.m].get() != 'None') and \
                 (self.m not in self.id_done): # add
                 self.id_done.append(self.m)
-                print(np.array(self.module_names)[self.m == np.array(self.id_mod)][0])
                 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:
@@ -185,7 +184,7 @@ def select(self, x: float, y:float):
                             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))
+                    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, 
@@ -219,8 +218,8 @@ def finnish(self):
                 (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(),
-                                                 {},
-                                                 np.array(self.module_list)[self.m == np.array(self.id_mod)][0],
+                                               {**self.req_settings, **self.opt_settings},
+                                                 np.array(self.module_names)[self.m == np.array(self.id_mod)][0],
                                                  True)
         self.check_quit()
         
@@ -233,7 +232,6 @@ def display_buttons(self):
         name = self.canvas.itemcget('t'+str(self.m), 'text')
         self.my_label.config(text = 'Choose a plugin for the '+name+' module')
         ps = PluginSpecs()
-        ps.print(ps.class_names)
         plugin_list = list(ps.class_names[module].values())
         plugin_list.append('Custom')
         descriptions = list(ps.class_descriptions[module].values())
@@ -255,44 +253,103 @@ def optionsWindow(self):
         """ Function to create a new window displaying the available options 
         of the selected plugin."""
         
-        print(self.plugin[self.m].get())
-        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")
-        
-        # Print options
-        tk.Label(self.newWindow,
-              text ="Please indicate your desired options for the "+self.plugin[self.m].get()+" plugin.\n\
-                  Close the window when you are done.", anchor = tk.W, justify=tk.LEFT).grid(row=0,column=0, columnspan=2)
-        options = {'arg1': 'int', 'arg2': ['C', 'F']}
+        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
-        self.entry = []
-        for arg, val in options.items():
-            tk.Label(self.newWindow,
-              text = arg).grid(row=r,column=0)
-            self.entry.append(tk.Entry(self.newWindow))
-            self.entry[-1].grid(row=r, column=1)
-            self.entry[-1].bind("<Return>", lambda event, a = r-1: self.on_return_entry(a))
-            r += 1
-        self.entry[0].focus()
+        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 
+        """ 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)-1:
-            self.entry[r+1].focus()
+        if r < len(self.entry):
+            self.entry[r].focus()
         else:
-            self.newWindow.focus()
+            self.finishButton.focus()
 
     def CreateToolTip(self, widget, text):
         """ Calls ToolTip to create a window with a widget description. """
@@ -517,13 +574,9 @@ def reset(self):
         """ Resets the canvas and the stored information."""
         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()
-            
+        if hasattr(self, 'newWindow') and (self.newWindow!= None):
+            self.newWindow.destroy()
+        
         self.canvas.startxy = []
         self.out_data = pd.DataFrame()
         self.connections = {}
@@ -557,8 +610,11 @@ def check_quit(self):
                 self.canvas.delete(tk.ALL)
                 self.saved = True
                 self.s.write_to_XML()
-                self.controller.Plugin.set(True)
                 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)
@@ -596,7 +652,34 @@ def hidetip(self):
         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/run_UI.py b/run_UI.py
index 9a2b88ec..3a6af7a9 100644
--- a/run_UI.py
+++ b/run_UI.py
@@ -3,4 +3,9 @@
 
 core = ai.Core()
 core.launch()
-core.run()
\ No newline at end of file
+# core.run()
+
+# ['State_a', 'Action_a']
+# ['State_x', 'State_y', 'Action_x', 'Action_y']
+
+# ['a', 'b', 'c', 'd']


Test patch:
diff --git a/test 4.xml b/test 4.xml
deleted file mode 100644
index 6c8617cb..00000000
--- a/test 4.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="Modelling" />
-            </relationships>
-            <coordinates>
-                [(300.0,50),0,{}]
-            </coordinates>
-        </Initialiser>
-        <Modelling name="Modelling">
-            <relationships>
-                <parent name="Initialiser" />
-                <child name="User Feetback" />
-            </relationships>
-            <coordinates>
-                [(184,155),2,{0:'d0-u2'}]
-            </coordinates>
-            <plugin type="Regression" />
-        </Modelling>
-        <UserFeedback name="User Feetback">
-            <relationships>
-                <parent name="Modelling" />
-                <child name="InputData" />
-            </relationships>
-            <coordinates>
-                [(397,244),3,{2:'r2-l3'}]
-            </coordinates>
-            <plugin type="CCA" />
-        </UserFeedback>
-        <InputData name="InputData">
-            <relationships>
-                <parent name="User Feetback" />
-                <child name="Output" />
-            </relationships>
-            <coordinates>
-                [(231,370),4,{3:'d3-u4'}]
-            </coordinates>
-            <plugin type="RF" />
-        </InputData>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="InputData" />
-            </relationships>
-            <coordinates>
-                [(300.0,550),1,{4:'d4-u1'}]
-            </coordinates>
-        </Output>
-    </pipeline>
-    <datastructure />
-</Settings>
diff --git a/test.xml b/test.xml
deleted file mode 100644
index 9542bd5c..00000000
--- a/test.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<Settings>
-    <pipeline>
-        <Initialiser name="Initialiser">
-            <plugin type="" />
-            <relationships>
-                <child name="Decision Making" />
-            </relationships>
-            <coordinates>
-                [(300.0, 50), 0, {}]
-            </coordinates>
-        </Initialiser>
-        <DecisionMaking name="Decision Making">
-            <plugin type="" />
-            <relationships>
-                <parent name="Initialiser" />
-                <child name="Output" />
-            </relationships>
-            <coordinates>
-                [(300.0, 300.0), 2, {0: 'd0-u2'}]
-            </coordinates>
-        </DecisionMaking>
-        <Output name="Output">
-            <plugin type="" />
-            <relationships>
-                <parent name="Decision Making" />
-            </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 4973d3ca..00000000
--- a/testing.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>
-                [(296, 203), 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>