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

Issue:
Cm fix tracker loops
# Purpose

 - Fix bugs that occur when running loops in `vai_lab_core.py` and in the progress tracker.
 - Additionally, improve progress tracker to operate without xml coordinates tag
 - Minor fixes for pybullet bugs

## Core Loop Error Messages
Previously, a missing module within a loop would throw an error stating that the loop type was unknown. This was a result of the try/except logic. This has now been fixed in `vai_lab_core.py` to show appropriate error messages

## Progress Tracker Improvements
Progress tracker would throw an error if the current xml did not contain coordinates tags for each module (as would happen if xml was created by hand). This PR fixes this and adds a new method of displaying progress in a list-format

## Pybullet bug
The method for launching pybullet without the GUI was not working, but now is fully functioning


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index df4253ce..e7337f16 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -17,7 +17,7 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.10", "3.11"]
     defaults:
       run:
         shell: bash
diff --git a/pyproject.toml b/pyproject.toml
index ea081a45..a3b61abd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,24 +12,23 @@ authors = [
 readme = "README.md"
 classifiers = [
     "License :: OSI Approved :: MIT License",
-    'Programming Language :: Python :: 3.8',
-    'Programming Language :: Python :: 3.9',
     'Programming Language :: Python :: 3.10',
     'Programming Language :: Python :: 3.11'
 ]
 requires-python = ">=3.8"
-version = "0.0.dev3"
+version = "0.0.dev4"
 description = "AI assisted Virtual Laboratory framework."
 
 dependencies = [
     "numpy >= 1.20.0",
     "pillow >= 9.0.0", # package name of PIL
     "pandas >= 1.4.0",
-    "sklearn >= 0.0",
+    "scikit-learn >= 0.0",
     "ttkwidgets >= 0.12.0",
     "matplotlib >= 3.5.0",
     "opencv-python >= 4.6.0.65",
-    "pytest >= 7.2.0"
+    "pytest >= 7.2.0",
+    "pybullet >= 3.2.5" 
 ]
 
 [project.urls]
diff --git a/src/vai_lab/Core/vai_lab_core.py b/src/vai_lab/Core/vai_lab_core.py
index 376e39b2..3d721ca7 100644
--- a/src/vai_lab/Core/vai_lab_core.py
+++ b/src/vai_lab/Core/vai_lab_core.py
@@ -1,22 +1,24 @@
+import time
+from sys import exit
+from os.path import join
+from typing import Dict, List, Tuple, Union
+
 from vai_lab._import_helper import import_module
-from vai_lab.Data.xml_handler import XML_handler
+from vai_lab._plugin_helpers import PluginSpecs
+from vai_lab._types import ModuleInterface, PluginSpecsInterface
 from vai_lab.GUI.GUI_core import GUI
 from vai_lab.Data.Data_core import Data
-from vai_lab._plugin_helpers import PluginSpecs
-from vai_lab._types import PluginSpecsInterface, ModuleInterface
-import time
-from sys import exit
+from vai_lab.Data.xml_handler import XML_handler
+
 
-from os.path import join
-from typing import Dict, Tuple, Union, List
 class Core:
     def __init__(self) -> None:
+        self.data = Data()
         self._xml_handler = XML_handler()
         self._avail_plugins: PluginSpecsInterface = PluginSpecs()
-        self.data = Data()
-        self.loop_level: int = 0
-        self.setup_complete: bool = False
         
+        self.loop_level: int = 0
+        self._initialised: bool = False
         self.status_logger:Dict = {}
         self._debug = False
 
@@ -26,7 +28,7 @@ def _launch(self):
         gui_app.set_avail_plugins(self._avail_plugins)
         gui_app.set_gui_as_startpage()
         gui_output = gui_app.launch()
-        if not gui_app.closed:
+        if not self._debug:
             try:
                 self.load_config_file(gui_output["xml_filename"])
             except:
@@ -34,14 +36,18 @@ def _launch(self):
             self._load_data()
 
     def load_config_file(self, filename: Union[str,List,Tuple]):
+        """Loads XML file into XML_handler object.
+        Parses filename first, if needed.
+        """
         if type(filename) == list or type(filename) == tuple:
             filedir:str = join(*filename)
         else:
             filedir = str(filename)
         self._xml_handler.load_XML(filedir)
-        self.setup_complete = True
+        self._initialised = True
 
     def _load_data(self) -> None:
+        """Loads data from XML file into Data object"""
         init_data_fn = self._xml_handler.data_to_load
         self.data.import_data_from_config(init_data_fn)
 
@@ -52,31 +58,32 @@ def _execute_module(self, specs):
         :param specs: dict of module to be executed
         """
         mod: ModuleInterface = import_module(globals(), specs["module_type"]).__call__()
+        mod._debug = self._debug
         mod.set_avail_plugins(self._avail_plugins)
         mod.set_data_in(self.data)
         mod.set_options(specs)
         print("\t"*self.loop_level
                 + specs["module_type"]
-                + " module: \"{}\"".format(specs["name"])
+                + " module: \"{}\" ".format(specs["name"])
                 + "processing..."
               )
         mod.launch()
         self.data = mod.get_result()
 
     def _execute_loop(self, specs):
-        try:
+        if  hasattr(self,"_execute_{}_loop".format(specs["type"])):
             print("\t"*self.loop_level
+                        + "Starting "
                         + specs["type"]
-                        + " loop: "
-                        + "\"{}\"".format(specs["name"])
-                        + " starting...")
+                        + " loop: \"{}\"".format(specs["name"])
+                        + " ...")
             self.loop_level += 1
             getattr(self, "_execute_{}_loop".format(specs["type"]))(specs)
             self.loop_level -= 1
-        except KeyError:
+        else:
             print("\nError: Invalid Loop Type.")
             print("Loop \"{0}\" with type \"{1}\" not recognised".format(
-                specs.key, specs["type"]))
+                specs["name"], specs["type"]))
 
     def _execute_entry_point(self, specs):
         """Placeholder: Will parse the initialiser module when ready"""
@@ -86,7 +93,7 @@ def _execute_exit_point(self, specs):
         """Placeholder: Will parse the Output module when ready"""
         pass
 
-    def _parse_condition(self, condition):
+    def _parse_loop_condition(self, condition):
         try:
             condition = int(condition)
             
@@ -98,20 +105,24 @@ def _parse_condition(self, condition):
                 "Other formats in in development. Only ranged for loops are working currently")
 
     def _execute_for_loop(self, specs):
-        condition = self._parse_condition(specs["condition"])
+        condition = self._parse_loop_condition(specs["condition"])
         for c in condition:
             self._execute(specs)
 
-    def _runTracker(self):
+    def _show_updated_tracker(self):
         self.gui_app = GUI()
+        self.gui_app._debug = self._debug
         self.gui_app.set_avail_plugins(self._avail_plugins)
-        self.gui_app.set_gui(self.status_logger)
+        self.gui_app.set_gui_as_progress_tracker(self.status_logger)
         self.gui_app._append_to_output("xml_filename", self._xml_handler.filename)
+        self.gui_app._load_plugin("progressTracker")
         return self.gui_app.launch()
     
     def _init_status(self, modules):
         for key in [key for key, val in modules.items() if type(val) == dict]:
             self.status_logger[modules[key]['name']] = {}
+            if modules[key]['class'] == "loop":
+                self._init_status(modules[key])
 
     def _add_status(self, module, key, value):
         self.status_logger[module['name']][key] = value
@@ -122,7 +133,6 @@ def _progress_start(self, module):
     def _progress_finish(self, module):
         self._add_status(module, 'finish', self._t)
 
-
     @property
     def _t(self):
         return time.strftime('%H:%M:%S', time.localtime())
@@ -141,18 +151,24 @@ def _execute(self, specs):
             self._progress_finish(specs[key])
 
             if specs[key]["class"] == 'module':
-                term = self._runTracker()
-                if not term['close']:
+                _tracker = self._show_updated_tracker()
+
+                if not _tracker['terminate']:
                     self.load_config_file(self._xml_handler.filename)
+                    # pass
                 else:
                     print('Pipeline terminated')
                     exit()
 
+    def _initialise_with_gui(self):
+        """Launches GUI when no XML file is specified"""
+        print("Loading GUI")
+        print("To load existing config, run core.load_config_file(<path_to_file>)")
+        self._launch()
+
     def run(self):
-        if not self.setup_complete:
-            print("No pipeline specified. Running GUI.")
-            print("To load existing config, run core.load_config_file(<path_to_file>)")
-            self._launch()
+        if not self._initialised:
+            self._initialise_with_gui()
         print("Running pipeline...")
         self._load_data()
         
diff --git a/src/vai_lab/Environment/plugins/PyBulletEnv.py b/src/vai_lab/Environment/plugins/PyBulletEnv.py
index b91b17a3..727f2d79 100644
--- a/src/vai_lab/Environment/plugins/PyBulletEnv.py
+++ b/src/vai_lab/Environment/plugins/PyBulletEnv.py
@@ -22,10 +22,11 @@ class PyBulletEnv(EnvironmentPluginT):
 
     def __init__(self) -> None:
         super().__init__(globals())
-        self.connection_mode = p.GUI
         self.model_ids: Dict = {}
 
     def set_gui(self, use_gui: bool = True):
+        if type(use_gui) == str:
+            use_gui = eval(use_gui)
         if use_gui:
             self.connection_mode = p.GUI  # Use pybullet GUI
         else:
diff --git a/src/vai_lab/GUI/GUI_core.py b/src/vai_lab/GUI/GUI_core.py
index a7b3b5a0..867dfab9 100644
--- a/src/vai_lab/GUI/GUI_core.py
+++ b/src/vai_lab/GUI/GUI_core.py
@@ -26,8 +26,8 @@ def __init__(self, *args, **kwargs):
         self._top_ui_layer = None
         self._module_config = None
         self._debug = False
-        self.closed = False
-        self.startpage = False
+        self._closed = False
+        self._is_startpage = False
         self.output = {}
 
     def set_avail_plugins(self, avail_plugins: PluginSpecsInterface):
@@ -45,15 +45,15 @@ def set_options(self, module_config: DictT):
         self._load_plugin(self._module_config["plugin"]["plugin_name"])
 
     def set_gui_as_startpage(self):
-        self.startpage = True
+        self._is_startpage = True
         self._load_plugin("main")
-        self.s = XML_handler()
-        self.s.new_config_file()
+        self.xml_handler = XML_handler()
+        self.xml_handler.new_config_file()
         
-    def set_gui(self, status):
-        self.startpage = True
+    def set_gui_as_progress_tracker(self, status):
+        self._is_startpage = True
         self._load_plugin("progressTracker")
-        self.status = status
+        self._status = status
 
     def _compare_layer_priority(self, ui_specs):
         """Check if a new module should have higher layer priority than the existing one
@@ -125,7 +125,7 @@ def _show_frame(self, page_name):
         frame.tkraise()
 
     def _on_closing(self) -> None:
-        self.closed = True
+        self._closed = True
         self.destroy()
 
     def destroy(self) -> None:
@@ -145,7 +145,7 @@ def launch(self):
             page_name = F.__name__
             frame = F(parent=container, controller=self,
                       config=self._module_config)
-            if not self.startpage:
+            if not self._is_startpage:
                 frame.set_data_in(self._data_in)
             self.frames[page_name] = frame
 
@@ -157,7 +157,7 @@ def launch(self):
         if not self._debug:
             self.mainloop()
         else:
-            self.closed = True
+            self._closed = True
         return self.output
 
     def get_result(self):
diff --git a/src/vai_lab/UserInteraction/plugins/CanvasInput.py b/src/vai_lab/UserInteraction/plugins/CanvasInput.py
index 8a333815..382e62b0 100644
--- a/src/vai_lab/UserInteraction/plugins/CanvasInput.py
+++ b/src/vai_lab/UserInteraction/plugins/CanvasInput.py
@@ -252,10 +252,7 @@ def draw_dot(self, event):
                     # Update coordinates in corresponding row if exists.
                     if self.tree[ii].selection():
                         n = int(self.tree[ii].selection()[0]) + 1
-                        if n % 2 == 0:
-                            tag = ('even',)
-                        else:
-                            tag = ('odd',)
+                        tag = ('odd' if n%2 == 0 else 'even',)
                         self.tree[ii].insert(
                             parent='', index='end', iid=n, text=n+1,
                             values=tuple(self.dict2mat(
diff --git a/src/vai_lab/_plugin_helpers.py b/src/vai_lab/_plugin_helpers.py
index bda7d5f1..90b4003d 100644
--- a/src/vai_lab/_plugin_helpers.py
+++ b/src/vai_lab/_plugin_helpers.py
@@ -1,8 +1,8 @@
-from typing import List, Iterator, Union
-from vai_lab._types import DictT
-
 import ast
 import os
+from typing import Iterator, List, Union
+
+from vai_lab._types import DictT
 
 
 class PluginSpecs(ast.NodeVisitor):
@@ -26,12 +26,16 @@ def _get_plugin_files(self, root_dir: str = None) -> None:
                 f for f in plugins if f.endswith(".py")]
 
     def _get_clean_module_dirs(self, root_dir: str) -> List[str]:
-        excludes = ["__init__.py", "__pycache__", "resources"]
+        excludes = ["tests", "examples", "resources", "run_pipeline.py"]
         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)
+            rm_list = []
+            for m in mod_dirs:
+                if m.startswith("_") \
+                    or m in excludes:
+                    rm_list.append(m)
+            for ex in rm_list:
+                mod_dirs.remove(ex)
             return mod_dirs
         else:
             return []
@@ -96,6 +100,11 @@ def visit_Assign(self, node):
                            "<ast expression>", "eval"))
                 self.available_plugins[self.curr_module][self.curr_plugin][key] = val
 
+    def _get_default_name_from_dict(self, plugin: DictT) -> str:
+        default = list(plugin.keys())\
+            [list(plugin.values()).index("default")]
+        return default
+
     def _get_option_specs(self, option: str) -> dict:
         """Gets either required OR optional settings for each plugin.
 
@@ -106,16 +115,16 @@ def _get_option_specs(self, option: str) -> dict:
 
         :returns output: nested dict of options by [Module][Plugin Class Name]
         """
-        output : DictT= {}
+        output: DictT = {}
         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]:
-                    cn = self.available_plugins[module][plugin]["_PLUGIN_CLASS_NAME"]
-                    output[module][cn] = self.available_plugins[module][plugin][option]
+                    rn = self._get_default_name_from_dict(self.available_plugins[module][plugin]["_PLUGIN_READABLE_NAMES"])
+                    output[module][rn] = self.available_plugins[module][plugin][option]
         return output
 
-    def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[DictT,None]:
+    def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[DictT, None]:
         for module in self.available_plugins.keys():
             for plugin in self.available_plugins[module].keys():
                 if tag in self.available_plugins[module][plugin]:
@@ -153,9 +162,7 @@ def available_plugin_names(self):
         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)
+                output.append(self._get_default_name_from_dict(names[n][plugin]))
         return output
 
     def find_from_class_name(self, value):
@@ -172,7 +179,8 @@ def print(self, value):
 
 if __name__ == "__main__":
     ps = PluginSpecs()
-    ps.print(ps.available_plugin_names)
+    # ps.print(ps.available_plugin_names)
+    ps.print(ps.optional_settings)
     # ps.print(ps.names)
     # ps.print(ps.class_descriptions)
     # print(list(ps.class_descriptions()['GUI'].values()))
diff --git a/src/vai_lab/_plugin_templates.py b/src/vai_lab/_plugin_templates.py
index 1b970685..ce6a7e5f 100644
--- a/src/vai_lab/_plugin_templates.py
+++ b/src/vai_lab/_plugin_templates.py
@@ -164,9 +164,16 @@ def _clean_solver_options(self):
         TODO: in future, reverse the direction of this function - instead pull all options
                 that are directly required by the solver
         """
-        return {i[0]: i[1]
+        _cleaned =  {i[0]: i[1]
                 for i in self._config["options"].items()
                 if i[0] not in self._options_to_ignore}
+        for key, val in _cleaned.items():
+            if val == 'True':
+                _cleaned[key] = True
+            elif val == 'False':
+                _cleaned[key] = False
+        return _cleaned
+            
 
     @abstractmethod
     def fit(self):
@@ -239,4 +246,4 @@ def set_gui(self,use_gui:bool=True) -> None:
     def configure(self, config: dict):
         """Extended from PluginTemplate.configure"""
         super().configure(config)
-        self.set_gui(self._config["options"]["headless"])
\ No newline at end of file
+        self.set_gui(self._config["options"]["usegui"])
\ No newline at end of file
diff --git a/src/vai_lab/_types.py b/src/vai_lab/_types.py
index 3db116d5..2d9d9470 100644
--- a/src/vai_lab/_types.py
+++ b/src/vai_lab/_types.py
@@ -110,6 +110,8 @@ def print(self, value):
 
 
 class ModuleInterface(Protocol):
+    _debug: bool
+
     def set_avail_plugins(self, avail_plugins: PluginSpecsInterface):
         ...
 
diff --git a/src/vai_lab/examples/xml_files/Hospital_use_case_visual.xml b/src/vai_lab/examples/xml_files/Hospital_use_case_visual.xml
deleted file mode 100644
index f8076273..00000000
--- a/src/vai_lab/examples/xml_files/Hospital_use_case_visual.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<pipeline>
-    <Initialiser name="Initialiser">
-        <plugin type="" />
-        <relationships>
-            <child name="Data preprocessing" />
-        </relationships>
-        <coordinates>
-            (314.5, 75.0)
-        </coordinates>
-    </Initialiser>
-    <DataProcessing name="Data preprocessing">
-        <plugin type="" />
-        <relationships>
-            <parent name="Initialiser" />
-            <child name="Modelling-0" />
-        </relationships>
-        <coordinates>
-            (117, 180)
-        </coordinates>
-    </DataProcessing>
-    <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>
-        <InputData name="Input data">
-            <plugin type="" />
-            <relationships>
-                <parent name="loop0" />
-                <child name="Modelling-1" />
-            </relationships>
-            <coordinates>
-                (322, 207)
-            </coordinates>
-        </InputData>
-        <UserInteraction name="User Feedback Adaptation">
-            <plugin type="" />
-            <relationships>
-                <parent name="Decision making" />
-                <parent name="loop0" />
-                <child name="Modelling-1" />
-            </relationships>
-            <coordinates>
-                (454, 325)
-            </coordinates>
-        </UserInteraction>
-        <DecisionMaking 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>
-        </DecisionMaking>
-    </loop>
-    <Output name="Output">
-        <plugin type="" />
-        <relationships>
-            <parent name="Decision making" />
-        </relationships>
-        <coordinates>
-            (314.5, 525.0)
-        </coordinates>
-    </Output>
-</pipeline>
\ No newline at end of file
diff --git a/src/vai_lab/examples/xml_files/Kevin_experimentalDesign.xml b/src/vai_lab/examples/xml_files/Kevin_experimentalDesign.xml
deleted file mode 100644
index bdb45db6..00000000
--- a/src/vai_lab/examples/xml_files/Kevin_experimentalDesign.xml
+++ /dev/null
@@ -1,163 +0,0 @@
-<pipeline>
-    <Initialiser name="Initialiser">
-        <relationships>
-            <child name="Input Data" />
-        </relationships>
-        <coordinates>
-                        [(350.0,50),0,{}]
-                    
-        </coordinates>
-    </Initialiser>
-    <InputData name="Input Data">
-        <plugin type="" />
-        <relationships>
-            <parent name="Initialiser" />
-            <child name="Data Processing" />
-        </relationships>
-        <coordinates>
-                        [(173,184),2,{0:'d0-u2'}]
-                    
-        </coordinates>
-    </InputData>
-    <DataProcessing name="Data Processing">
-        <plugin type="" />
-        <relationships>
-            <parent name="Input Data" />
-            <child name="Modelling" />
-        </relationships>
-        <coordinates>
-                        [(173,294),3,{2:'d2-u3'}]
-                    
-        </coordinates>
-    </DataProcessing>
-    <Modelling name="Modelling">
-        <plugin type="" />
-        <relationships>
-            <parent name="Data Processing" />
-            <parent name="User Interaction" />
-            <child name="Decision Making" />
-        </relationships>
-        <coordinates>
-                        [(175,409),4,{3:'d3-u4',5:'l5-r4'}]
-                    
-        </coordinates>
-    </Modelling>
-    <UserInteraction name="User Interaction">
-        <plugin type="" />
-        <relationships>
-            <parent name="Decision Making" />
-            <child name="Modelling" />
-        </relationships>
-        <coordinates>
-                        [(543,414),5,{6:'r6-d5'}]
-                    
-        </coordinates>
-    </UserInteraction>
-    <DecisionMaking name="Decision Making">
-        <plugin type="" />
-        <relationships>
-            <parent name="Modelling" />
-            <child name="Output" />
-            <child name="User Interaction" />
-        </relationships>
-        <coordinates>
-                        [(351,522),6,{4:'d4-l6'}]
-                    
-        </coordinates>
-    </DecisionMaking>
-    <Output name="Output">
-        <plugin type="" />
-        <relationships>
-            <parent name="Decision Making" />
-        </relationships>
-        <coordinates>
-                        [(350.0,650),1,{6:'d6-u1'}]
-                    
-        </coordinates>
-    </Output>
-    <loop type="for" condition="number of experiments" name="loop0">
-        <relationships>
-            <child name="Input Data" />
-            <child name="Data Processing" />
-            <child name="Modelling" />
-            <child name="User Interaction" />
-            <child name="Decision Making" />
-        </relationships>
-        <coordinates>
-                        (48.0,110.0,651.0,572.0)
-                    
-        </coordinates>
-        <InputData name="Input Data">
-            <plugin type="" />
-            <relationships>
-                <parent name="Initialiser" />
-                <parent name="loop0" />
-                <child name="Data Processing" />
-            </relationships>
-            <coordinates>
-                                [(173,184),2,{0:'d0-u2'}]
-                            
-            </coordinates>
-        </InputData>
-        <DataProcessing name="Data Processing">
-            <plugin type="" />
-            <relationships>
-                <parent name="Input Data" />
-                <parent name="loop0" />
-                <child name="Modelling" />
-            </relationships>
-            <coordinates>
-                                [(173,294),3,{2:'d2-u3'}]
-                            
-            </coordinates>
-        </DataProcessing>
-        <Modelling name="Modelling">
-            <plugin type="" />
-            <relationships>
-                <parent name="Data Processing" />
-                <parent name="User Interaction" />
-                <parent name="loop0" />
-                <child name="Decision Making" />
-            </relationships>
-            <coordinates>
-                                [(173,404),4,{3:'d3-u4',5:'l5-r4'}]
-                            
-            </coordinates>
-        </Modelling>
-        <UserInteraction name="User Interaction">
-            <plugin type="" />
-            <relationships>
-                <parent name="Decision Making" />
-                <parent name="loop0" />
-                <child name="Modelling" />
-            </relationships>
-            <coordinates>
-                                [(536,410),5,{6:'r6-d5'}]
-                            
-            </coordinates>
-        </UserInteraction>
-        <DecisionMaking name="Decision Making">
-            <plugin type="" />
-            <relationships>
-                <parent name="Modelling" />
-                <parent name="loop0" />
-                <child name="Output" />
-                <child name="User Interaction" />
-            </relationships>
-            <coordinates>
-                                [(351,522),6,{4:'d4-l6'}]
-                            
-            </coordinates>
-        </DecisionMaking>
-    </loop>
-    <Output name="Output">
-        <plugin type="" />
-        <relationships>
-            <parent name="Decision Making" />
-        </relationships>
-        <coordinates>
-                        [(350.0,650),1,{6:'d6-u1'}]
-                    
-        </coordinates>
-    </Output>
-</pipeline>
diff --git a/src/vai_lab/examples/xml_files/basic_operation.xml b/src/vai_lab/examples/xml_files/basic_operation.xml
deleted file mode 100644
index 82219316..00000000
--- a/src/vai_lab/examples/xml_files/basic_operation.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<pipeline>
-    <Initialiser name="Init">
-        <inputdata>
-            <X file="./examples/image_classification/training_images/" />
-            <Y file="./examples/image_classification/Y_data.csv" />
-        </inputdata>
-        <relationships>
-            <child name="My First UserInteraction Module" />
-        </relationships>
-    </Initialiser>
-    <loop type="for" condition="2" name="For Loop 1">
-        <UserInteraction name="My First UserInteraction Module">
-            <plugin type="manual">
-            </plugin>
-            <relationships>
-                <parent name="Init" />
-                <child name="Data Processing 1" />
-            </relationships>
-        </UserInteraction>
-        <loop type="for" condition="1" name="For Loop 2">
-            <UserInteraction name="My Second UserInteraction Module">
-                <plugin type="canvas">
-                    <class_list>
-                        ['State_a','Action_a']
-                        ['State_x','State_y','Action_x','Action_y']
-                        ['State_x','State_y','Action_x','Action_y']
-                    </class_list>
-                </plugin>
-                <relationships>
-                    <parent name="Data Processing 1" />
-                    <child name="Data Processing 1" />
-                    <child name="Output" />
-                </relationships>
-            </UserInteraction>
-        </loop>
-    </loop>
-    <Output>
-        <plugin type="">
-            <out_data>
-                all
-            </out_data>
-            <save_to file="../resources/output_test.csv" />
-        </plugin>
-        <relationships>
-            <parent name="My Second UserInteraction Module" />
-        </relationships>
-    </Output>
-</pipeline>
diff --git a/src/vai_lab/examples/xml_files/canvas_demo.xml b/src/vai_lab/examples/xml_files/canvas_demo.xml
index 04028d1c..5ed1f55d 100644
--- a/src/vai_lab/examples/xml_files/canvas_demo.xml
+++ b/src/vai_lab/examples/xml_files/canvas_demo.xml
@@ -24,7 +24,7 @@
             <save_to file="../resources/output_test.csv" />
         </plugin>
         <relationships>
-            <parent name="My Second UserFeedback Module" />
+            <parent name="My First UserFeedback Module" />
         </relationships>
     </Output>
 </pipeline>
diff --git a/src/vai_lab/examples/xml_files/pybullet_env_example.xml b/src/vai_lab/examples/xml_files/pybullet_env_example.xml
index 45b8ef36..886051e0 100644
--- a/src/vai_lab/examples/xml_files/pybullet_env_example.xml
+++ b/src/vai_lab/examples/xml_files/pybullet_env_example.xml
@@ -20,9 +20,9 @@
                  plane.urdf
                  ./Environment/resources/models/half_cheetah_with_mass.xml
             </model_dir>
-            <headless>
+            <usegui>
                  False
-            </headless>
+            </usegui>
             <setGravity>
                     0.0
                     0.0
@@ -32,14 +32,14 @@
                     0.01
             </timestep>
             <max_steps>
-                    1000
+                    10
             </max_steps>            
         </plugin>
     </Environment>
     <Output name="Output">
         <plugin type="" />
         <relationships>
-            <parent name="Modelling" />
+            <parent name="MyEnv" />
         </relationships>
         <coordinates>
             [(300.0,550),1,{2:'d2-u1'}]
diff --git a/src/vai_lab/examples/xml_files/random_forest_class_demo.xml b/src/vai_lab/examples/xml_files/random_forest_class_demo.xml
index 04dd8952..e7c0d450 100644
--- a/src/vai_lab/examples/xml_files/random_forest_class_demo.xml
+++ b/src/vai_lab/examples/xml_files/random_forest_class_demo.xml
@@ -4,10 +4,10 @@
             <child name="My Data Processing" />
         </relationships>
 		<inputdata>
-            <X file="./supervised_classification/X_tr.csv"/>
-            <Y file="./supervised_classification/Y_tr.csv"/>
-            <X_test file="./supervised_classification/X_tst.csv"/>
-            <Y_test file="./supervised_classification/Y_tst.csv"/>
+            <X file="./examples/supervised_classification/X_tr.csv"/>
+            <Y file="./examples/supervised_classification/Y_tr.csv"/>
+            <X_test file="./examples/supervised_classification/X_tst.csv"/>
+            <Y_test file="./examples/supervised_classification/Y_tst.csv"/>
         </inputdata>
     </Initialiser>
   
@@ -35,7 +35,7 @@
         <relationships>
             <parent name="My Modelling Module" />
         </relationships>
-      <outData file="./my_results.csv" />
+      <!-- <outData file="./my_results.csv" /> -->
     </Output>
   
 </pipeline>
diff --git a/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml b/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml
index b8131122..777ca31c 100644
--- a/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml
+++ b/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml
@@ -37,7 +37,7 @@
         </coordinates>
         <plugin type="StandardScaler">
             <with_mean>
-                 
+                 True
             </with_mean>
         </plugin>
     </DataProcessing>
diff --git a/src/vai_lab/examples/xml_files/scalar_demo.xml b/src/vai_lab/examples/xml_files/scalar_demo.xml
index e2e7e318..10e3f914 100644
--- a/src/vai_lab/examples/xml_files/scalar_demo.xml
+++ b/src/vai_lab/examples/xml_files/scalar_demo.xml
@@ -21,7 +21,11 @@
         <coordinates>
             [(300.0,300.0),2,{0:'d0-u2'}]
         </coordinates>
-        <plugin type="StandardScaler" />
+        <plugin type="StandardScaler">
+            <with_mean>
+                True
+            </with_mean>
+        </plugin>
     </DataProcessing>
     <Output name="Output">
         <plugin type="" />
diff --git a/src/vai_lab/examples/xml_files/user_feedback_demo.xml b/src/vai_lab/examples/xml_files/user_feedback_demo.xml
index 16098dff..6d1a21a6 100644
--- a/src/vai_lab/examples/xml_files/user_feedback_demo.xml
+++ b/src/vai_lab/examples/xml_files/user_feedback_demo.xml
@@ -25,7 +25,7 @@
             <save_to file="../resources/output_test.csv" />
         </plugin>
         <relationships>
-            <parent name="My Second UserInteraction Module" />
+            <parent name="My First UserInteraction Module" />
         </relationships>
     </Output>
 </pipeline>
diff --git a/src/vai_lab/utils/plugins/MainPage.py b/src/vai_lab/utils/plugins/MainPage.py
index 375a141b..92d366c3 100644
--- a/src/vai_lab/utils/plugins/MainPage.py
+++ b/src/vai_lab/utils/plugins/MainPage.py
@@ -31,7 +31,10 @@ def __init__(self, parent, controller, config: dict):
         self.controller.output_type = 'regression'
         self.out_data = pd.DataFrame()
         self.bg = parent['bg']
+        if not self.controller._debug:
+            self._setup_frame()
 
+    def _setup_frame(self):
         self.script_dir = get_lib_parent_dir()
         self.my_img1 = ImageTk.PhotoImage(
             Image.open(
diff --git a/src/vai_lab/utils/plugins/aidCanvas.py b/src/vai_lab/utils/plugins/aidCanvas.py
index c02ea06f..3ff9954d 100644
--- a/src/vai_lab/utils/plugins/aidCanvas.py
+++ b/src/vai_lab/utils/plugins/aidCanvas.py
@@ -34,6 +34,21 @@ def __init__(self, parent, controller, config: dict):
         self.bg = parent['bg']
         self.controller = controller
 
+        self.canvas_startxy: List[Tuple] = []
+        self.out_data = pd.DataFrame()
+        self.connections: DictT = {}
+        self.modules = 0
+        self.module_list: List[str] = []
+        self.module_names: List[str] = []
+
+        # Create module
+        self.w, self.h = 100, 50
+        self.cr = 4
+
+        if not self.controller._debug:
+            self._setup_frame()
+
+    def _setup_frame(self):
         script_dir = get_lib_parent_dir()
         self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage(
             file=os.path.join(os.path.join(
@@ -69,17 +84,6 @@ def __init__(self, parent, controller, config: dict):
         #     height=self.height, background="white")
         self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10)
 
-        self.canvas_startxy: List[Tuple] = []
-        self.out_data = pd.DataFrame()
-        self.connections: DictT = {}
-        self.modules = 0
-        self.module_list: List[str] = []
-        self.module_names: List[str] = []
-
-        # 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)
diff --git a/src/vai_lab/utils/plugins/pluginCanvas.py b/src/vai_lab/utils/plugins/pluginCanvas.py
index 518a00ec..2a9ed825 100644
--- a/src/vai_lab/utils/plugins/pluginCanvas.py
+++ b/src/vai_lab/utils/plugins/pluginCanvas.py
@@ -32,7 +32,19 @@ def __init__(self, parent, controller, config: dict):
         self.parent = parent
         self.controller = controller
         self.s = XML_handler()
+        self.m: int
+        self.w, self.h = 100, 50
+        self.cr = 4
+        self.id_done = [0, 1]
+        self.id_mod:List[int] = [0, 1]
+        self.out_data = pd.DataFrame()
+        self.plugin: Dict[int, tk.StringVar] = {}
+        self.dataType: Dict[int, tk.StringVar] = {}
+        self.allWeHearIs: List[tk.Radiobutton] = []
+        if not self.controller._debug:
+            self._setup_frame()
 
+    def _setup_frame(self):
         script_dir = get_lib_parent_dir()
         self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage(
             file=os.path.join(os.path.join(
@@ -57,16 +69,8 @@ def __init__(self, parent, controller, config: dict):
                                 height=self.height, background="white")
         self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10)
 
-        self.m: int
-        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:List[int] = [0, 1]
-        self.out_data = pd.DataFrame()
-        self.plugin: Dict[int, tk.StringVar] = {}
-        self.dataType: Dict[int, tk.StringVar] = {}
-        self.allWeHearIs: List[tk.Radiobutton] = []
+        
 
         self.my_label = tk.Label(self.frame2,
                                  text='',
diff --git a/src/vai_lab/utils/plugins/progressTracker.py b/src/vai_lab/utils/plugins/progressTracker.py
index d495ab3b..4dc7a6a0 100644
--- a/src/vai_lab/utils/plugins/progressTracker.py
+++ b/src/vai_lab/utils/plugins/progressTracker.py
@@ -6,7 +6,6 @@
 import numpy as np
 import pandas as pd
 from PIL import Image, ImageTk
-from vai_lab._plugin_helpers import PluginSpecs
 from vai_lab.Data.xml_handler import XML_handler
 from vai_lab._import_helper import get_lib_parent_dir
 
@@ -28,27 +27,27 @@ def __init__(self, parent, controller, config: dict):
         super().__init__(parent, bg=parent['bg'])
         self.bg = parent['bg']
         self.controller = controller
-        self.s = XML_handler()
+        self.xml_handler = XML_handler()
         self.controller.title('Progress Tracker')
         
-        script_dir = os.path.dirname(__file__)
-        self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage(
-            file=os.path.join(os.path.join(
-                get_lib_parent_dir(),
-                'utils',
-                'resources',
-                'Assets',
-                'VAILabsIcon.ico'))))
+        if not self.controller._debug:
+            self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage(
+                file=os.path.join(os.path.join(
+                    get_lib_parent_dir(),
+                    'utils',
+                    'resources',
+                    'Assets',
+                    'VAILabsIcon.ico'))))
         self.grid_columnconfigure(0, weight=1)
 
-        frame1 = tk.Frame(self, bg=self.bg)
-        self.frame2 = tk.Frame(self, bg=self.bg)
+        self.frame1 = tk.Frame(self, bg=self.bg)
+        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 = 700, 700
-        self.canvas = tk.Canvas(frame1, width=self.width,
+        self.canvas = tk.Canvas(self.frame1, width=self.width,
                                 height=self.height, background="white")
         self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10)
 
@@ -62,11 +61,9 @@ def __init__(self, parent, controller, config: dict):
         self.canvas.bind('<Button-1>', self.on_click)
         self.dataType: Dict = {}
         
-        self.upload()
-
         sec = 5
         self.click = False
-        self.my_label = tk.Label(self.frame2, 
+        self.my_label = tk.Label(frame2, 
                     text = 
                     'This window will get\nclosed after '+str(sec)+' seconds\nunless you click on the canvas.',
                     pady= 10,
@@ -76,6 +73,9 @@ def __init__(self, parent, controller, config: dict):
                     anchor = tk.CENTER)
         self.my_label.grid(column = 0,
                             row = 0, columnspan = 2, padx=10)
+        
+        self.upload()
+
         tk.Button(
             self.frame4, text = 'Next step', fg = 'white', bg = parent['bg'], 
             height = 3, width = 15, font = self.controller.pages_font, 
@@ -85,11 +85,11 @@ def __init__(self, parent, controller, config: dict):
             height = 3, width = 15, font = self.controller.pages_font, 
             command = self.terminate).grid(column = 0, row = 26, sticky="news", pady=(0,10))
         
-        self.controller._append_to_output('close', False)
+        self.controller._append_to_output('terminate', False)
         self.save_path = ''
         self.saved = True
-        frame1.grid(column=0, row=0, sticky="nsew")
-        self.frame2.grid(column=1, row=0, sticky="new")
+        self.frame1.grid(column=0, row=0, sticky="nsew")
+        frame2.grid(column=1, row=0, sticky="new")
         frame3.grid(column=0, row=1, sticky="swe")
         self.frame4.grid(column=1, row=1, sticky="sew")
         
@@ -98,10 +98,6 @@ def __init__(self, parent, controller, config: dict):
 
         self.controller.after(sec*1000,lambda:self.check_click())
 
-    def class_list(self,value):
-        """ Temporary fix """
-        return value
-
     def on_click(self, event):
         """ Passes the mouse click coordinates to the select function."""
         self.click = True
@@ -114,7 +110,7 @@ def check_click(self):
 
     def terminate(self):
         """ Terminates window and pipeline. """
-        self.controller._append_to_output('close', True)
+        self.controller._append_to_output('terminate', True)
         self.check_quit()
         
     def select(self, x: float, y:float):
@@ -185,8 +181,8 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False):
             tag = ('n0',)
         text_w = self.controller.pages_font.measure(boxName+'-00') + 10
         # Check module status
-        if 'start' in self.controller.status[boxName]:
-            if 'finish' in self.controller.status[boxName]:
+        if 'start' in self.controller._status[boxName]:
+            if 'finish' in self.controller._status[boxName]:
                 colour = '#46da63'
             else:
                 colour = '#dbaa21'
@@ -214,9 +210,9 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False):
             justify = tk.CENTER)
         
         CanvasTooltip(self.canvas, self.canvas.find_withtag('p'+str(self.modules))[0], 
-                           text = 'Model progress status:\n'+self.pretty_status(self.controller.status[boxName])) # Link box
+                           text = 'Model progress status:\n'+self.pretty_status(self.controller._status[boxName])) # Link box
         CanvasTooltip(self.canvas, self.canvas.find_withtag('t'+str(self.modules))[0], 
-                           text = 'Model progress status:\n'+self.pretty_status(self.controller.status[boxName])) # Link text
+                           text = 'Model progress status:\n'+self.pretty_status(self.controller._status[boxName])) # Link text
         
         if not out:
             self.canvas.create_oval(
@@ -260,19 +256,20 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False):
         self.canvas_startxy.append((x, y))
         self.connections[self.modules] = {}
         self.module_out(boxName)
-        self.module_list.append(boxName)
+        # self.module_list.append(boxName)
         self.modules += 1
         
     def optionsWindow(self):
         """ Function to create a new window displaying the available options 
         of the selected plugin."""
         
-        self.module = np.array(self.module_list)[self.m == np.array(self.id_mod)][0]
-        self.plugin = self.s.loaded_modules[self.module]['plugin']['plugin_name']
-        module_type = self.s.loaded_modules[self.module]['module_type']
-        ps = PluginSpecs()
-        self.opt_settings = ps.optional_settings[module_type][self.plugin]
-        self.req_settings = ps.required_settings[module_type][self.plugin]
+        mod_idx = np.where(self.m == np.array(self.id_mod))[0][0]
+        self.module = np.array(self.module_list)[mod_idx]
+        self.plugin = np.array(self.plugin_list)[mod_idx]
+        module_type = np.array(self.type_list)[mod_idx]
+        
+        self.opt_settings = self.controller._avail_plugins.optional_settings[module_type][self.plugin]
+        self.req_settings = self.controller._avail_plugins.required_settings[module_type][self.plugin]
         if (len(self.opt_settings) != 0) or (len(self.req_settings) != 0):
             if hasattr(self, 'newWindow') and (self.newWindow!= None):
                 self.newWindow.destroy()
@@ -288,7 +285,7 @@ def optionsWindow(self):
                     'Assets',
                     'VAILabsIcon.ico'))))
             self.newWindow.geometry("350x400")
-
+            self.raise_above_all(self.newWindow)
             frame1 = tk.Frame(self.newWindow)
             frame4 = tk.Frame(self.newWindow)
             
@@ -309,7 +306,7 @@ def optionsWindow(self):
 
             frame2 = tk.Frame(self.newWindow, bg='green')
             self.r = 1
-            self.create_treeView(frame2)
+            self.create_treeView(frame2, ['Name', 'Type', 'Value'])
             self.fill_treeview(frame2, self.req_settings, self.opt_settings)
             frame2.grid(column=0, row=1, sticky="nswe", pady=10, padx=10)
 
@@ -329,7 +326,11 @@ def optionsWindow(self):
             self.newWindow.grid_rowconfigure(1, weight=2)
             self.newWindow.grid_columnconfigure(0, weight=1)
 
-    def create_treeView(self, tree_frame):
+    def raise_above_all(self, window):
+        window.attributes('-topmost', 1)
+        window.attributes('-topmost', 0)
+
+    def create_treeView(self, tree_frame, columns_names):
         """ Function to create a new tree view in the given frame
 
         Parameters
@@ -353,7 +354,6 @@ def create_treeView(self, tree_frame):
         tree_scrollx.config(command=self.tree.xview)
         tree_scrolly.config(command=self.tree.yview)
 
-        columns_names = ['Name', 'Type', 'Value']
         self.tree['columns'] = columns_names
 
         # Format columns
@@ -381,7 +381,6 @@ def OnDoubleClick(self, event):
         """ Executed when a row of the treeview is double clicked.
         Opens an entry box to edit a cell. """
 
-        # ii = self.notebook.index(self.notebook.select())
         self.treerow = self.tree.identify_row(event.y)
         self.treecol = self.tree.identify_column(event.x)
         tags = self.tree.item(self.treerow)["tags"]
@@ -515,6 +514,41 @@ def on_return_entry(self, r):
         else:
             self.finishButton.focus()
 
+    def isKey(self, d, k, key_list, cond_list):
+        """ Checks if the elements of a dictionary hava a specific key 
+        : param d: dict type.
+        : param k: string type.      
+        : param key_list: list type with keys that are not loop.
+        : param cond_list: list type with whether k is in a module in key_list.
+        """
+        for key in [key for key, val in d.items() if type(val) == dict]:
+            if d[key]['class'] == 'loop':
+                key_list, cond_list = self.isKey(d[key],k,key_list,cond_list)
+            else:
+                if d[key]['class'].lower() == 'entry_point':
+                    self.init_module = key
+                elif d[key]['class'].lower()  == 'exit_point':
+                    self.out_module = key
+                key_list.append(key)
+                cond_list.append(k in d[key].keys())
+        return key_list, cond_list
+    
+    def allKeysinDict(self, d, k, out_list):
+        """ Stores the elements of a dictionary for a specific key 
+        : param d: dict type.
+        : param k: string type.      
+        : param out_list: list type to store the keys that fulfill the condition.
+        """
+        for key in [key for key, val in d.items() if type(val) == dict]:
+            if d[key]['class'] == 'loop':
+                out_list = self.allKeysinDict(d[key],k,out_list)
+            else:
+                if k in d[key].keys():
+                    out_list.append(d[key][k])
+                else:
+                    out_list.append({})
+        return out_list
+    
     def upload(self):
         """ Opens the XML file that was previously uploaded and places the 
         modules, loops and connections in the canvas."""
@@ -523,39 +557,133 @@ def upload(self):
 
         self.reset()
 
-        self.s = XML_handler()
-        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)
+        self.xml_handler = XML_handler()
+        self.xml_handler.load_XML(filename)
+        modules = self.xml_handler.loaded_modules
+        self.module_list, self.isCoords = self.isKey(modules, 'coordinates', [], [])
+        self.p_list = self.allKeysinDict(modules, 'plugin', [])
+        self.plugin_list = []
+        for e in self.p_list:
+            if type(e) == dict and 'plugin_name' in e.keys():
+                self.plugin_list.append(e['plugin_name'])
+            else:
+                self.plugin_list.append(0)
+        self.type_list = self.allKeysinDict(modules, 'module_type', [])
+
+        if all(self.isCoords):
+
+            # Place Initialiser and Output
+            self.add_module(self.init_module, self.width/2, self.h, ini=True)
+            self.add_module(self.out_module, self.width/2, self.height - self.h, out=True)
+
+            modout = modules[self.out_module]
+            del modules[self.init_module], modules[self.out_module] # They are generated when resetting
+            self.disp_mod = [self.init_module, self.out_module]
+            self.id_mod = [0, 1]
+            # Reorder to have input and output in first two positions
+            self.plugin_list.insert(1, self.plugin_list.pop(len(self.plugin_list)-1))
+            self.type_list.insert(1, self.type_list.pop(len(self.type_list)-1))
+
+            # Place the modules
+            self.place_modules(modules)
+
+            #Output module
+            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]
+
+        else: # There are no coordinates for some modules.
+            self.my_label.config(text = " ".join(self.my_label.cget('text').split(' ')[:-1] + ['list.']))
+
+            self.update_output(modules)
+            self.id_mod = list(range(len(self.module_list)))
+            self.disp_mod = self.module_list
+            self.canvas.pack_forget()
+            frame_tree = tk.Frame(self.frame1, bg='green')
+            self.create_treeView(frame_tree, ['Module'])
+            self.r=0
+            for i, module in enumerate(self.module_list):
+                self.tree.insert(parent='', index='end', iid=str(self.r), text='',
+                                    values=tuple([module]), tags=('odd' if i%2 == 0 else 'even',))
+                self.r+=1
+            self.tree.column(
+                'Module', width=int(self.controller.pages_font.measure(str(max(self.module_list, key=len))))+20,
+                minwidth=50, anchor=tk.CENTER)
+            self.tree.tag_configure('odd', foreground='black',
+                                    background='#9fc5e8')
+            self.tree.tag_configure('even', foreground='black',
+                                    background='#cfe2f3')
+
+            frame_tree.grid(column=0, row=1, sticky="nswe", pady=10, padx=10)
+            frame_tree.grid_rowconfigure(tuple(range(len(self.module_list))), weight=1)
+            frame_tree.grid_columnconfigure(tuple(range(2)), weight=1)
+            self.tree.bind('<Button-1>', self.on_click_noCanvas)
+
+    def on_click_noCanvas(self, event):
+        """ Passes the mouse click coordinates to the select function when there is no Canvas."""
+        self.click = True
+        self.select_noCanvas(event.x, event.y)
+        
+    def select_noCanvas(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.treerow = self.tree.identify_row(y)
+        self.treecol = self.tree.identify_column(x)
+        tags = self.tree.item(self.treerow)["tags"]
+
+        if len(tags) > 0:
+            self.m = int(self.treerow)
+            if int(self.m) > 0 and int(self.m) < len(self.module_list)-1:
+                self.optionsWindow()
+
+    def update_output(self, modules: dict):
+        """Update the output.
+        :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'])))))
+                self.loops.append({'type': modules[key]['type'],
+                                   'condition': modules[key]['condition'],
+                                   'mod': []})
+                self.update_output(modules[key])
+            else:
+                self.module_out(modules[key]['name'])
+                # Connect modules
+                for p, parent in enumerate(modules[key]['parents']):
+                    if not (parent[:4] == 'loop'):
+                        self.out_data.loc[parent].loc[modules[key]['name']] = 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)
 
     def place_modules(self, modules: dict):
         """Places the modules in the dictionary in the canvas.
@@ -644,11 +772,10 @@ def reset(self):
         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.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 = []
@@ -795,4 +922,4 @@ def hide(self):
 
 if __name__ == "__main__":
     app = progressTracker()
-    app.mainloop()
+    app.mainloop()
\ No newline at end of file


Test patch:
diff --git a/src/vai_lab/tests/test_examples.py b/src/vai_lab/tests/test_examples.py
new file mode 100644
index 00000000..b23eece1
--- /dev/null
+++ b/src/vai_lab/tests/test_examples.py
@@ -0,0 +1,18 @@
+from glob import glob
+
+import vai_lab as ai
+from vai_lab._import_helper import get_lib_parent_dir
+
+def test_examples():
+    """
+    Test launching GUI
+    """
+    scripts = glob(get_lib_parent_dir() + "/examples/xml_files/" + "*")
+    for file in scripts:
+        """Temp removal of userfeedback test - relies too heavily on GUI for github actions"""
+        if "user_feedback" not in file:
+            print (file)
+            core = ai.Core()
+            core.load_config_file(file)
+            core._debug = True
+            core.run()
\ No newline at end of file