baseexperimentgui.cpp
1 /********************************************************************************
2  * FARSA *
3  * Copyright (C) 2007-2012 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
6  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21  ********************************************************************************/
22 
23 #include "baseexperimentgui.h"
24 #include <QVBoxLayout>
25 #include <QGridLayout>
26 #include <cmath>
27 
28 namespace farsa {
29 
30 BaseExperimentGUI::BaseExperimentGUI(BaseExperiment* experiment, QWidget* parent, Qt::WindowFlags flags) :
31  QWidget(parent, flags),
32  DataUploaderDownloader<__BaseExperiment_internal::OperationControl, __BaseExperiment_internal::OperationStatus>(1, BlockUploader, this, experiment->getUploaderDownloader()),
33  m_experiment(experiment),
34  m_operations(),
35  m_operationSelectionGroup(NULL),
36  m_operationsListLabel(NULL),
37  m_operationsList(NULL),
38  m_selectedOperationInfo(NULL),
39  m_startSelectedOperationButton(NULL),
40  m_currentOperationControlGroup(NULL),
41  m_currentOperationInfo(NULL),
42  m_stopCurrentOperationButton(NULL),
43  m_steppableOperationControlGroup(NULL),
44  m_pauseOperationButton(NULL),
45  m_stepOperationButton(NULL),
46  m_delaySliderLabel(NULL),
47  m_delaySlider(NULL),
48  m_runningOperationID(-1)
49 {
50  // The main layout
51  QVBoxLayout* mainLayout = new QVBoxLayout(this);
52 
53  {
54  // The group of widgets to select an operation
55  m_operationSelectionGroup = new QGroupBox("Operation Selection", this);
56  mainLayout->addWidget(m_operationSelectionGroup);
57 
58  // The layout for the m_operationSelectionGroup
59  QGridLayout* layout = new QGridLayout(m_operationSelectionGroup);
60 
61  // The label for the operations combo box
62  m_operationsListLabel = new QLabel("Operation: ", m_operationSelectionGroup);
63  layout->addWidget(m_operationsListLabel, 0, 0);
64 
65  // The combo box displaying the list of operations
66  m_operationsList = new QComboBox(m_operationSelectionGroup);
67  QSizePolicy operationsListSizePolicy = m_operationsList->sizePolicy();
68  operationsListSizePolicy.setHorizontalStretch(1);
69  m_operationsList->setSizePolicy(operationsListSizePolicy);
70  connect(m_operationsList, SIGNAL(currentIndexChanged(int)), this, SLOT(currentOperationChanged(int)));
71  layout->addWidget(m_operationsList, 0, 1);
72 
73  // The label displaying information about the currently selected operation
74  m_selectedOperationInfo = new QLabel("Invalid operation", m_operationSelectionGroup);
75  layout->addWidget(m_selectedOperationInfo, 1, 0, 1, 2);
76 
77  // The button to start the currently selected operation
78  m_startSelectedOperationButton = new QPushButton("Start Operation", m_operationSelectionGroup);
79  connect(m_startSelectedOperationButton, SIGNAL(clicked()), this, SLOT(startOperation()));
80  layout->addWidget(m_startSelectedOperationButton, 2, 0, 1, 2);
81  }
82 
83  {
84  // The group of widgets to control the current operation
85  m_currentOperationControlGroup = new QGroupBox("Current Operation Control", this);
86  mainLayout->addWidget(m_currentOperationControlGroup);
87 
88  // The layout for the m_currentOperationControlGroup
89  QVBoxLayout* layout = new QVBoxLayout(m_currentOperationControlGroup);
90 
91  // The label displaying information about the running operation
92  m_currentOperationInfo = new QLabel("No running operation", m_currentOperationControlGroup);
93  layout->addWidget(m_currentOperationInfo);
94 
95  // The button to stop the running operation
96  m_stopCurrentOperationButton = new QPushButton("Stop Operation", m_currentOperationControlGroup);
97  connect(m_stopCurrentOperationButton, SIGNAL(clicked()), this, SLOT(stopOperation()));
98  layout->addWidget(m_stopCurrentOperationButton);
99  }
100 
101  {
102  // The group of widgets to control a steppable operation
103  m_steppableOperationControlGroup = new QGroupBox("Steppable Operation Control", this);
104  mainLayout->addWidget(m_steppableOperationControlGroup);
105 
106  // The layout for the m_steppableOperationControlGroup
107  QGridLayout* layout = new QGridLayout(m_steppableOperationControlGroup);
108 
109  // The button to put a steppable operation in pause
110  m_pauseOperationButton = new QPushButton("Pause", m_steppableOperationControlGroup);
111  m_pauseOperationButton->setCheckable(true);
112  connect(m_pauseOperationButton, SIGNAL(clicked(bool)), this, SLOT(pauseOperation(bool)));
113  layout->addWidget(m_pauseOperationButton, 0, 0, 1, 2);
114 
115  // The button to perform a step of a steppable operation
116  m_stepOperationButton = new QPushButton("Step", m_steppableOperationControlGroup);
117  connect(m_stepOperationButton, SIGNAL(clicked()), this, SLOT(stepOperation()));
118  layout->addWidget(m_stepOperationButton, 1, 0, 1, 2);
119 
120  // The label for the delay slider
121  m_delaySliderLabel = new QLabel("Interval between steps:", m_steppableOperationControlGroup);
122  layout->addWidget(m_delaySliderLabel, 2, 0);
123 
124  // The slider to set the delay between steps of a steppable operation
125  m_delaySlider = new QSlider(Qt::Horizontal, m_steppableOperationControlGroup);
126  QSizePolicy delaySliderSizePolicy = m_delaySlider->sizePolicy();
127  delaySliderSizePolicy.setHorizontalStretch(1);
128  m_delaySlider->setSizePolicy(delaySliderSizePolicy);
129  m_delaySlider->setRange(0, 100);
130  m_delaySlider->setValue(delayToSliderValue(m_experiment->currentInterval()));
131  connect(m_delaySlider, SIGNAL(valueChanged(int)), this, SLOT(changeDelay(int)));
132  layout->addWidget(m_delaySlider, 2, 1);
133  }
134 
135  // Updating the list of operations
136  updateOperationsList();
137 }
138 
140 {
141  // Nothing to do here
142 }
143 
145 {
147  // We need a cylce because there could be more than one datum, when the uploader is connected
148  // (see dataexchange.h documentation for more information)
150  while ((d = downloadDatum()) != NULL) {
151  switch (d->status) {
153  updateOperationsList();
154  break;
156  m_runningOperationID = d->operationID;
157  m_currentOperationInfo->setText(describeOperation(m_runningOperationID, "Running operation: "));
158 
159  if (m_operations[m_runningOperationID]->getUseSeparateThread()) {
160  if (m_operations[m_runningOperationID]->getSteppable()) {
161  enableWidgets(false, true, true, true);
162  } else {
163  enableWidgets(false, true, false, false);
164  }
165  } else {
166  enableWidgets(false, false, false, false);
167  }
168  break;
170  m_runningOperationID = -1;
171  currentOperationChanged(m_operationsList->currentIndex());
172  m_currentOperationInfo->setText("No running operation");
173  break;
175  m_stepOperationButton->setEnabled(true);
176  break;
178  m_stepOperationButton->setEnabled(false);
179  break;
181  // Blocking signals of the slider to avoid re-setting the value in the simulation
182  m_delaySlider->blockSignals(true);
183  m_delaySlider->setValue(delayToSliderValue(d->delay));
184  m_delaySlider->blockSignals(false);
185  break;
186  }
187  }
188 
189  return true;
190  } else {
191  return QWidget::event(e);
192  }
193 }
194 
195 void BaseExperimentGUI::currentOperationChanged(int index)
196 {
197  m_selectedOperationInfo->setText(describeOperation(((index < 0) ? index : (index + 1)), "Selected operation: "));
198 
199  if (m_runningOperationID == -1) {
200  if (index == -1) {
201  enableWidgets(false, false, false, false);
202  } else {
203  if (m_operations[index + 1]->getUseSeparateThread()) {
204  if (m_operations[index + 1]->getSteppable()) {
205  enableWidgets(true, false, true, true);
206  } else {
207  enableWidgets(true, false, false, false);
208  }
209  } else {
210  enableWidgets(true, false, false, false);
211  }
212  }
213  }
214 }
215 
216 void BaseExperimentGUI::startOperation()
217 {
218  if (m_operationsList->currentIndex() == -1) {
219  return;
220  }
221 
222  // Telling the experiment to start an operation
223  DatumToUpload d(*this);
224  if (m_pauseOperationButton->isChecked()) {
226  } else {
228  }
229  d->operationID = m_operationsList->currentIndex() + 1;
230 
231  m_startSelectedOperationButton->setEnabled(false);
232 }
233 
234 void BaseExperimentGUI::stopOperation()
235 {
236  // Telling the experiment to stop the current operation
237  DatumToUpload d(*this);
239 }
240 
241 void BaseExperimentGUI::pauseOperation(bool pause)
242 {
243  // Telling the experiment to pause/resume the current operation
244  DatumToUpload d(*this);
245 
246  if (pause) {
248  if (m_runningOperationID != -1) {
249  m_stepOperationButton->setEnabled(true);
250  }
251  } else {
253  m_stepOperationButton->setEnabled(false);
254  }
255 }
256 
257 void BaseExperimentGUI::stepOperation()
258 {
259  // Telling the experiment to perform a step
260  DatumToUpload d(*this);
262 }
263 
264 void BaseExperimentGUI::changeDelay(int delay)
265 {
266  // Telling the experiment the new delay
267  DatumToUpload d(*this);
269  d->interval = sliderValueToDelay(delay);
270 }
271 
272 void BaseExperimentGUI::updateOperationsList()
273 {
274  m_operations = m_experiment->getOperations();
275 
276  // First of all clearing the combobox
277  m_operationsList->clear();
278 
279  // Now adding all operations except the first one that is always stop
280  for (int i = 1; i < m_operations.size(); i++) {
281  m_operationsList->insertItem(i - 1, m_operations[i]->getName());
282  }
283 
284  // Selecting the first operation
285  m_operationsList->setCurrentIndex(0);
286 }
287 
288 void BaseExperimentGUI::enableWidgets(bool enableStart, bool enableStop, bool enablePause, bool enableDelay)
289 {
290  m_startSelectedOperationButton->setEnabled(enableStart);
291  m_stopCurrentOperationButton->setEnabled(enableStop);
292  m_pauseOperationButton->setEnabled(enablePause);
293  if ((enablePause) && (m_runningOperationID != -1)) {
294  m_stepOperationButton->setEnabled(m_pauseOperationButton->isChecked());
295  } else {
296  m_stepOperationButton->setEnabled(false);
297  }
298  m_delaySlider->setEnabled(enableDelay);
299 }
300 
301 QString BaseExperimentGUI::describeOperation(int id, QString prefix) const
302 {
303  QString str;
304 
305  if (id < 0) {
306  str = "Invalid operation";
307  } else {
308  str = prefix + m_operations[id]->getName();
309  if (m_operations[id]->getUseSeparateThread()) {
310  if (m_operations[id]->getSteppable()) {
311  str += "; it is a STEPPABLE THREAD operation";
312  } else {
313  str += "; it is a THREAD operation";
314  }
315  } else {
316  str += "; it is an IMMEDIATE operation";
317  }
318  }
319 
320  return str;
321 }
322 
323 unsigned long BaseExperimentGUI::sliderValueToDelay(int value) const
324 {
325  if (value == 0) {
326  return 0;
327  } else {
328  return (unsigned long) pow((double) 1.1, (double) value);
329  }
330 }
331 
332 int BaseExperimentGUI::delayToSliderValue(unsigned long interval) const
333 {
334  if (interval == 0) {
335  return 0;
336  } else {
337  return (int) (log((double) interval) / log((double) 1.1));
338  }
339 }
340 
341 } // End namespace farsa
342