Skip to content

Commit e57da85

Browse files
committed
Added Cancel Experiment button functionality
* #35
1 parent c24004d commit e57da85

File tree

4 files changed

+100
-19
lines changed

4 files changed

+100
-19
lines changed

MLC/GUI/Autogenerated/untitled/experiment_in_progress.ui

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,6 @@
8484
<string>Individuals' Costs</string>
8585
</property>
8686
<layout class="QVBoxLayout" name="picture_layout">
87-
<item>
88-
<widget class="QCheckBox" name="log_check">
89-
<property name="text">
90-
<string>Log Scale</string>
91-
</property>
92-
</widget>
93-
</item>
9487
<item>
9588
<widget class="QTextEdit" name="indiv_chart"/>
9689
</item>
@@ -135,7 +128,7 @@
135128
<item>
136129
<widget class="QPushButton" name="cancel_button">
137130
<property name="text">
138-
<string>Stop Experiment</string>
131+
<string>Cancel Experiment</string>
139132
</property>
140133
</widget>
141134
</item>

MLC/GUI/Autogenerated/untitled/untitled.pro.user

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!DOCTYPE QtCreatorProject>
3-
<!-- Written by QtCreator 4.2.0, 2017-01-13T23:28:08. -->
3+
<!-- Written by QtCreator 4.2.0, 2017-01-15T22:01:26. -->
44
<qtcreator>
55
<data>
66
<variable>EnvironmentId</variable>

MLC/GUI/Experiment/ExperimentDialog.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
class ExperimentDialog(QMainWindow):
33-
experiment_finished = pyqtSignal()
33+
experiment_finished = pyqtSignal([bool])
3434
MAX_GENERATIONS = 30
3535

3636
def __init__(self, mlc_local,
@@ -443,10 +443,14 @@ def _ask_if_experiment_config_must_be_saved(self):
443443

444444
self._autogenerated_object.save_config_button.setDisabled(True)
445445

446-
def _update_experiment(self):
446+
def _update_experiment(self, cancelled):
447447
self._progress_dialog.close_window()
448-
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
449-
'Experiment was succesfully executed.', QMessageBox.Ok)
448+
if not cancelled:
449+
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
450+
'Experiment was succesfully executed.', QMessageBox.Ok)
451+
else:
452+
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
453+
'Experiment was cancelled by the user.', QMessageBox.Ok)
450454

451455
self._update_individuals_per_generation_list()
452456
self._update_experiment_info()

MLC/GUI/Experiment/ExperimentInProgress.py

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import numpy as np
55
import os
66
import sys
7+
import threading
78
sys.path.append(os.path.abspath(".") + "/../..")
89

910
from MLC.Application import MLC_CALLBACKS
@@ -19,10 +20,58 @@
1920
from PyQt5.QtWidgets import QMessageBox
2021

2122
from threading import Thread
22-
23+
from threading import Condition
2324
logger = 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+
2675
class 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

Comments
 (0)