44import numpy as np
55import os
66import sys
7+ import threading
78sys .path .append (os .path .abspath ("." ) + "/../.." )
89
910from MLC .Application import MLC_CALLBACKS
1920from PyQt5 .QtWidgets import QMessageBox
2021
2122from threading import Thread
22-
23+ from threading import Condition
2324logger = get_gui_logger ()
2425
2526
27+ class ThreadCancelException (Exception ):
28+
29+ def __init__ (self ):
30+ Exception .__init__ (self , "[EXPERIMENT_IN_PROGRESS] [THREAD_CANCELLED] - "
31+ "Thread was cancelled by the user" )
32+
33+
34+ class ExperimentCondition ():
35+
36+ def __init__ (self ):
37+ self ._condition = Condition ()
38+ self ._experiment_stopped = False
39+ self ._experiment_cancelled = False
40+
41+ def stop_experiment (self ):
42+ self ._condition .acquire ()
43+ self ._experiment_stopped = True
44+ self ._condition .release ()
45+
46+ def continue_experiment (self ):
47+ self ._condition .acquire ()
48+ self ._experiment_stopped = False
49+ self ._condition .notify ()
50+ self ._condition .release ()
51+
52+ def cancel_experiment (self ):
53+ self ._condition .acquire ()
54+ self ._experiment_cancelled = True
55+ self ._condition .notify ()
56+ self ._condition .release ()
57+
58+ def wait_if_experiment_stopped (self ):
59+ had_to_wait = False
60+ self ._condition .acquire ()
61+ if self ._experiment_stopped :
62+ self ._condition .wait ()
63+ had_to_wait = True
64+ self ._condition .release ()
65+ return had_to_wait
66+
67+ def experiment_cancelled (self ):
68+ cancelled = None
69+ self ._condition .acquire ()
70+ cancelled = self ._experiment_cancelled
71+ self ._condition .release ()
72+ return cancelled
73+
74+
2675class ExperimentInProgressDialog (QMainWindow ):
2776 indiv_evaluated = pyqtSignal ([int , int , int , float ])
2877 new_generation = pyqtSignal ()
@@ -51,15 +100,31 @@ def __init__(self, parent, parent_signal, chart_params):
51100 self ._chart_params = chart_params
52101 self ._current_generation = 1
53102
54- # FIXME: Change this for a kwargs implementation
103+ # Condition variable used to check when the experiment has been stopped or cancelled
104+ self ._experiment_condition = ExperimentCondition ()
105+
55106 def add_experiment_data (self , amount_gens , indivs_per_gen ):
56107 self ._amount_gens = amount_gens
57108 self ._indivs_per_gen = indivs_per_gen
58109 self ._total_indivs = self ._amount_gens * self ._indivs_per_gen
59110 self ._create_new_chart ()
60111
112+ def get_experiment_condition_variable (self ):
113+ return self ._experiment_condition
114+
61115 def on_cancel_button_clicked (self ):
62116 logger .debug ('{0} [CANCEL_BUTTON] - Executing on_cancel_button_clicked function' .format (self ._log_prefix ))
117+ self ._experiment_condition .stop_experiment ()
118+
119+ response = QMessageBox .information (self , "Experiment Stopped" ,
120+ "Do you want to stop the current experiment?" ,
121+ QMessageBox .Yes | QMessageBox .No ,
122+ QMessageBox .No )
123+
124+ if response == QMessageBox .Yes :
125+ self ._experiment_condition .cancel_experiment ()
126+ else :
127+ self ._experiment_condition .continue_experiment ()
63128
64129 def _update_dialog (self , indivs_per_gen_counter , total_indivs_counter , gen_counter , cost ):
65130 logger .debug ('{0} [UPDATE_EXP_IN_PROGRESS] - Executing update_dialog function' .format (self ._log_prefix ))
@@ -80,7 +145,7 @@ def _update_dialog(self, indivs_per_gen_counter, total_indivs_counter, gen_count
80145
81146 def _simulation_finished (self ):
82147 logger .debug ('{0} [SIM_FINISHED] - Executing simulation_finished function' .format (self ._log_prefix ))
83- self ._parent_signal .emit ()
148+ self ._parent_signal .emit (self . _experiment_condition . experiment_cancelled () )
84149
85150 def _update_current_gen_experiment (self , indiv_index , cost ):
86151 if cost > self ._chart_params ["max_cost" ]:
@@ -165,15 +230,23 @@ def __init__(self, mlc_local, experiment_name,
165230 self ._dialog = ExperimentInProgressDialog (parent = parent ,
166231 parent_signal = parent_signal ,
167232 chart_params = chart_params )
233+ self ._experiment_condition = self ._dialog .get_experiment_condition_variable ()
168234 self ._dialog .add_experiment_data (self ._amount_gens , self ._indivs_per_gen )
169235 self ._dialog .show ()
170236
171237 def run (self ):
172238 logger .debug ('{0} [RUN] - Executing Thread mainloop' .format (self ._log_prefix ))
173- self ._mlc_local .go (self ._experiment_name , self ._to_gen , self ._from_gen , self ._callbacks )
239+ try :
240+ self ._mlc_local .go (self ._experiment_name , self ._to_gen ,
241+ self ._from_gen , self ._callbacks )
242+ except ThreadCancelException , err :
243+ logger .info ('{0} [RUN] - Thread was cancelled by the user'
244+ .format (self ._log_prefix ))
245+ self ._dialog .simulation_finished .emit ()
174246
175247 def indiv_evaluated (self , individual_id , cost ):
176- logger .debug ('{0} [INDIV_EV] - Executing indiv_evaluated callback. Indiv Id: {1} - Cost: {2}'
248+ logger .debug ('{0} [INDIV_EV] - Executing indiv_evaluated callback. '
249+ 'Indiv Id: {1} - Cost: {2}'
177250 .format (self ._log_prefix , individual_id , cost ))
178251
179252 self ._indiv_per_gen_counter += 1
@@ -182,19 +255,30 @@ def indiv_evaluated(self, individual_id, cost):
182255 self ._total_indivs_counter ,
183256 self ._gen_counter ,
184257 cost )
258+ self ._check_if_project_stopped_or_cancelled ()
185259
186260 def new_generation_created (self , generation_number ):
187- logger .debug ('{0} [NEW_GEN_CREATED] - Executing new_generation_created function' .format (self ._log_prefix ))
261+ logger .debug ('{0} [NEW_GEN_CREATED] - '
262+ 'Executing new_generation_created function'
263+ .format (self ._log_prefix ))
188264 self ._gen_counter += 1
189265 self ._indiv_per_gen_counter = 0
190266 self ._dialog .new_generation .emit ()
267+ self ._check_if_project_stopped_or_cancelled ()
191268
192269 def simulation_started (self ):
193270 logger .debug ('{0} [SIM_STARTED] - Executing simulation_started function' .format (self ._log_prefix ))
271+ self ._check_if_project_stopped_or_cancelled ()
194272
195273 def simulation_finished (self ):
196274 logger .debug ('{0} [SIM_FINISHED] - Executing simulation_finished function' .format (self ._log_prefix ))
197275 self ._dialog .simulation_finished .emit ()
198276
199277 def close_window (self ):
200278 self ._dialog .close ()
279+
280+ def _check_if_project_stopped_or_cancelled (self ):
281+ if self ._experiment_condition .wait_if_experiment_stopped ():
282+ # Check if the experiment was cancelled
283+ if self ._experiment_condition .experiment_cancelled ():
284+ raise ThreadCancelException ()
0 commit comments