22
33import os
44import numpy as np
5+ import shutil
56import sys
67import time
7- sys .path .append (os .path .abspath ("." ) + "/../.." )
88
99from MLC .Log .log import get_gui_logger
1010from MLC .GUI .Autogenerated .autogenerated import Ui_ExperimentWindow
2121from MLC .Population .Evaluation .EvaluatorFactory import EvaluatorFactory
2222from MLC .Population .Population import Population
2323from PyQt5 .QtCore import pyqtSignal
24+ from PyQt5 .QtCore import QFileSystemWatcher
25+ # from PyQt5.QtCore import QHeaderView
2426from PyQt5 .QtCore import Qt
2527from PyQt5 .QtCore import QUrl
2628from PyQt5 .QtGui import QDesktopServices
2729from PyQt5 .QtWidgets import QAbstractItemView
30+ from PyQt5 .QtWidgets import QFileDialog
2831from PyQt5 .QtWidgets import QMainWindow
2932from PyQt5 .QtWidgets import QMessageBox
3033from PyQt5 .QtWidgets import QInputDialog
@@ -73,6 +76,16 @@ def __init__(self, mlc_local,
7376 # Signal to be emitted when the experiment is closed
7477 self ._experiment_closed_signal = experiment_closed_signal
7578
79+ # Watch changes in the experiment config file
80+ self ._file_watcher = QFileSystemWatcher ()
81+ conf_path_list = [self ._mlc_local .get_working_dir (),
82+ self ._experiment_name ,
83+ self ._experiment_name + ".conf" ]
84+ # Use the splat operator to dearrange the list
85+ self ._experiment_conf_path = os .path .join (* conf_path_list )
86+ self ._file_watcher .addPath (self ._experiment_conf_path )
87+ self ._file_watcher .fileChanged .connect (self ._reload_experiment_config )
88+
7689 # Experiment in progress chart configuration
7790 self ._chart_conf = ChartConfiguration (self ._autogenerated_object )
7891 indivs_per_gen = Config .get_instance ().getint ("POPULATION" , "size" )
@@ -83,6 +96,7 @@ def closeEvent(self, event):
8396 logger .debug ('[EXPERIMENT {0}] [CLOSE_DIALOG] - Executing overriden closeEvent function' .format (self ._experiment_name ))
8497 self ._ask_if_experiment_config_must_be_saved ()
8598 # Close the experiment
99+ self ._file_watcher .removePath (self ._experiment_conf_path )
86100 self ._mlc_local .close_experiment (self ._experiment_name )
87101 self ._experiment_closed_signal .emit (self ._experiment_name )
88102
@@ -204,7 +218,12 @@ def on_dimension_check_clicked(self):
204218 def on_save_config_button_clicked (self ):
205219 logger .debug ('[EXPERIMENT {0}] [SAVE_CONFIG_BUTTON_CLICKED] - Executing on_save_config_button_clicked function'
206220 .format (self ._experiment_name ))
221+
222+ # Remove the config file from the Qt File watcher momentarily
223+ # to avoid rendering unnecesary warnings
224+ self ._file_watcher .removePath (self ._experiment_conf_path )
207225 self ._persist_experiment_config ()
226+ self ._file_watcher .addPath (self ._experiment_conf_path )
208227
209228 def on_tab_changed (self , tab_index ):
210229 logger .debug ('[EXPERIMENT {0}] [TAB_CHANGED] - Executing on_tab_changed function'
@@ -215,10 +234,52 @@ def on_import_config_button_clicked(self):
215234 logger .debug ('[EXPERIMENT {0}] [IMPORT_CONFIG_BUTTON_CLICKED] - Executing on_import_config_button_clicked function'
216235 .format (self ._experiment_name ))
217236
237+ # Get the path of the experiment to import
238+ config_file_path = QFileDialog .getOpenFileName (self , "Import Experiment" , "." ,
239+ "Config File (*.conf)" )[0 ]
240+ if not config_file_path :
241+ # User clicked 'Cancel' or simply closed the Dialog
242+ return
243+
244+ self ._file_watcher .removePath (self ._experiment_conf_path )
245+ self ._mlc_local .set_experiment_configuration_from_file (self ._experiment_name ,
246+ config_file_path )
247+ self ._load_experiment_config ()
248+ QMessageBox .information (self , "Config File Imported" ,
249+ "New config file {0} was succesfully imported" .format (config_file_path ))
250+ logger .info ("[EXPERIMENT {0}] [EXPORT_CONFIG] - New config file {1} was "
251+ "succesfully imported." .format (self ._experiment_name , config_file_path ))
252+ self ._autogenerated_object .save_config_button .setDisabled (True )
253+ self ._file_watcher .addPath (self ._experiment_conf_path )
254+
218255 def on_export_config_button_clicked (self ):
219256 logger .debug ('[EXPERIMENT {0}] [EXPORT_CONFIG_BUTTON_CLICKED] - Executing on_export_config_button_clicked function'
220257 .format (self ._experiment_name ))
221258
259+ export_dir = QFileDialog .getExistingDirectory (self , 'Choose the directory where to export the configuration file' ,
260+ '.' , QFileDialog .ShowDirsOnly )
261+ if not export_dir :
262+ # User clicked 'Cancel' or simply closed the Dialog
263+ return
264+
265+ export_file_name = os .path .join (export_dir , self ._experiment_name + ".conf" )
266+ try :
267+ shutil .copyfile (self ._experiment_conf_path , export_file_name )
268+ except Exception , err :
269+ QMessageBox .critical (self , "Config File Not Exported" ,
270+ "Config File could not be exported. "
271+ "Error {0}" .format (err ))
272+ logger .error ("[EXPERIMENT {0}] [EXPORT_CONFIG] - Config file could not "
273+ "be exported. Error: {0}" .format (err ))
274+ return
275+
276+ logger .info ("[EXPERIMENT {0}] [EXPORT_CONFIG] - Config file was "
277+ "succesfully exported. Location: {1}"
278+ .format (self ._experiment_name , export_file_name ))
279+ QMessageBox .information (self , "Config File Exported" ,
280+ "Config File was succesfully exported. You can find it at {0}"
281+ .format (export_file_name ))
282+
222283 def on_ev_edit_button_clicked (self ):
223284 logger .debug ('[EXPERIMENT {0}] [EV_EDIT_BUTTON_CLICKED] - Executing on_ev_edit_button_clicked function'
224285 .format (self ._experiment_name ))
@@ -313,6 +374,12 @@ def on_board_config_button_clicked(self):
313374 board_config_window = BoardConfigurationWindow (parent = self )
314375 board_config_window .show ()
315376
377+ def on_edit_config_button_clicked (self ):
378+ logger .debug ('[EXPERIMENT {0}] [EDIT_CONFIG_BUTTON] - '
379+ 'Executing on_edit_config_button_clicked function'
380+ .format (self ._experiment_name ))
381+ QDesktopServices .openUrl (QUrl (self ._experiment_conf_path ))
382+
316383 def _config_table_edited (self , left , right ):
317384 config_table = self ._autogenerated_object .config_table
318385 table_model = config_table .model ()
@@ -472,15 +539,23 @@ def _update_experiment_info(self):
472539 number_of_gens = experiment_info ["generations" ]
473540 if number_of_gens == 0 :
474541 from_gen_combo .addItems ([str (1 )])
475- to_gen_combo .addItems ([str (x ) for x in xrange (2 , ExperimentWindow .MAX_GENERATIONS )])
542+ to_gen_combo .addItems ([str (x ) for x in xrange (2 , ExperimentWindow .MAX_GENERATIONS + 1 )])
476543 else :
477544 from_gen_combo .addItems ([str (x ) for x in xrange (1 , number_of_gens + 1 )])
478- to_gen_combo .addItems ([str (x ) for x in xrange (2 , ExperimentWindow .MAX_GENERATIONS )])
545+ to_gen_combo .addItems ([str (x ) for x in xrange (2 , ExperimentWindow .MAX_GENERATIONS + 1 )])
479546
480547 # Set the from gen combo to the last generation evaluated
481548 index = from_gen_combo .findText (str (number_of_gens ), Qt .MatchFixedString )
482549 from_gen_combo .setCurrentIndex (index )
483550
551+ # set the to_gen_combo to the lest generation evaluated + 1
552+ to_gen_index = number_of_gens + 1
553+ if number_of_gens == ExperimentWindow .MAX_GENERATIONS :
554+ to_gen_index = ExperimentWindow .MAX_GENERATIONS
555+
556+ index = to_gen_combo .findText (str (to_gen_index ), Qt .MatchFixedString )
557+ to_gen_combo .setCurrentIndex (index )
558+
484559 # Fill the db_view
485560 gen_count_group = self ._autogenerated_object .gen_count_group
486561 if number_of_gens != 0 :
@@ -557,6 +632,23 @@ def _update_individuals_figure(self):
557632 # Add the Indiv Canvas
558633 chart_layout .addWidget (indiv_canvas )
559634
635+ def _reload_experiment_config (self , some_string ):
636+ logger .debug ('[EXPERIMENT {0}] [EXPERIMENT_CONF_FILE_CHANGED] - '
637+ 'Executing _experiment_conf_file_changed function'
638+ .format (self ._experiment_name ))
639+
640+ self ._mlc_local .reload_experiment_configuration (self ._experiment_name )
641+ self ._load_experiment_config ()
642+
643+ QMessageBox .warning (self , "Experiment Config Changed" , "The Experiment config file "
644+ "has changed. The configuration will be reloaded. "
645+ "The changes made will be discarded" )
646+ logger .info ('[EXPERIMENT {0}] [RELOAD_CONFIG] - Config file has changed. Configuration will be reloaded' )
647+ self ._autogenerated_object .save_config_button .setDisabled (True )
648+ # FIXME: Dunno why?, but I have to add the config file to the
649+ # watcher in order to work again the next time
650+ self ._file_watcher .addPath (self ._experiment_conf_path )
651+
560652 def _persist_experiment_config (self ):
561653 try :
562654 self ._mlc_local .set_experiment_configuration (self ._experiment_name , self ._experiment_config )
@@ -579,7 +671,9 @@ def _ask_if_experiment_config_must_be_saved(self):
579671 QMessageBox .Yes | QMessageBox .No ,
580672 QMessageBox .Yes )
581673 if response == QMessageBox .Yes :
674+ self ._file_watcher .removePath (self ._experiment_conf_path )
582675 self ._persist_experiment_config ()
676+ self ._file_watcher .addPath (self ._experiment_conf_path )
583677 else :
584678 self ._load_experiment_config ()
585679
0 commit comments