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),
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  // Associating data uploaders and downloaders
52 
53  // The main layout
54  QVBoxLayout* mainLayout = new QVBoxLayout(this);
55 
56  {
57  // The group of widgets to select an operation
58  m_operationSelectionGroup = new QGroupBox("Operation Selection", this);
59  mainLayout->addWidget(m_operationSelectionGroup);
60 
61  // The layout for the m_operationSelectionGroup
62  QGridLayout* layout = new QGridLayout(m_operationSelectionGroup);
63 
64  // The label for the operations combo box
65  m_operationsListLabel = new QLabel("Operation: ", m_operationSelectionGroup);
66  layout->addWidget(m_operationsListLabel, 0, 0);
67 
68  // The combo box displaying the list of operations
69  m_operationsList = new QComboBox(m_operationSelectionGroup);
70  QSizePolicy operationsListSizePolicy = m_operationsList->sizePolicy();
71  operationsListSizePolicy.setHorizontalStretch(1);
72  m_operationsList->setSizePolicy(operationsListSizePolicy);
73  connect(m_operationsList, SIGNAL(currentIndexChanged(int)), this, SLOT(currentOperationChanged(int)));
74  layout->addWidget(m_operationsList, 0, 1);
75 
76  // The label displaying information about the currently selected operation
77  m_selectedOperationInfo = new QLabel("Invalid operation", m_operationSelectionGroup);
78  layout->addWidget(m_selectedOperationInfo, 1, 0, 1, 2);
79 
80  // The button to start the currently selected operation
81  m_startSelectedOperationButton = new QPushButton("Start Operation", m_operationSelectionGroup);
82  connect(m_startSelectedOperationButton, SIGNAL(clicked()), this, SLOT(startOperation()));
83  layout->addWidget(m_startSelectedOperationButton, 2, 0, 1, 2);
84  }
85 
86  {
87  // The group of widgets to control the current operation
88  m_currentOperationControlGroup = new QGroupBox("Current Operation Control", this);
89  mainLayout->addWidget(m_currentOperationControlGroup);
90 
91  // The layout for the m_currentOperationControlGroup
92  QVBoxLayout* layout = new QVBoxLayout(m_currentOperationControlGroup);
93 
94  // The label displaying information about the running operation
95  m_currentOperationInfo = new QLabel("No running operation", m_currentOperationControlGroup);
96  layout->addWidget(m_currentOperationInfo);
97 
98  // The button to stop the running operation
99  m_stopCurrentOperationButton = new QPushButton("Stop Operation", m_currentOperationControlGroup);
100  connect(m_stopCurrentOperationButton, SIGNAL(clicked()), this, SLOT(stopOperation()));
101  layout->addWidget(m_stopCurrentOperationButton);
102  }
103 
104  {
105  // The group of widgets to control a steppable operation
106  m_steppableOperationControlGroup = new QGroupBox("Steppable Operation Control", this);
107  mainLayout->addWidget(m_steppableOperationControlGroup);
108 
109  // The layout for the m_steppableOperationControlGroup
110  QGridLayout* layout = new QGridLayout(m_steppableOperationControlGroup);
111 
112  // The button to put a steppable operation in pause
113  m_pauseOperationButton = new QPushButton("Pause", m_steppableOperationControlGroup);
114  m_pauseOperationButton->setCheckable(true);
115  connect(m_pauseOperationButton, SIGNAL(clicked(bool)), this, SLOT(pauseOperation(bool)));
116  layout->addWidget(m_pauseOperationButton, 0, 0, 1, 2);
117 
118  // The button to perform a step of a steppable operation
119  m_stepOperationButton = new QPushButton("Step", m_steppableOperationControlGroup);
120  connect(m_stepOperationButton, SIGNAL(clicked()), this, SLOT(stepOperation()));
121  layout->addWidget(m_stepOperationButton, 1, 0, 1, 2);
122 
123  // The label for the delay slider
124  m_delaySliderLabel = new QLabel("Interval between steps:", m_steppableOperationControlGroup);
125  layout->addWidget(m_delaySliderLabel, 2, 0);
126 
127  // The slider to set the delay between steps of a steppable operation
128  m_delaySlider = new QSlider(Qt::Horizontal, m_steppableOperationControlGroup);
129  QSizePolicy delaySliderSizePolicy = m_delaySlider->sizePolicy();
130  delaySliderSizePolicy.setHorizontalStretch(1);
131  m_delaySlider->setSizePolicy(delaySliderSizePolicy);
132  m_delaySlider->setRange(0, 100);
133  m_delaySlider->setValue(delayToSliderValue(m_experiment->currentInterval()));
134  connect(m_delaySlider, SIGNAL(valueChanged(int)), this, SLOT(changeDelay(int)));
135  layout->addWidget(m_delaySlider, 2, 1);
136  }
137 
138  // Updating the list of operations
139  updateOperationsList();
140 }
141 
143 {
144  // Nothing to do here
145 }
146 
148 {
150  // We need a cylce because there could be more than one datum, when the uploader is connected
151  // (see dataexchange.h documentation for more information)
153  while ((d = downloadDatum()) != NULL) {
154  switch (d->status) {
156  updateOperationsList();
157  break;
159  m_runningOperationID = d->operationID;
160  m_currentOperationInfo->setText(describeOperation(m_runningOperationID, "Running operation: "));
161 
162  if (m_operations[m_runningOperationID]->getUseSeparateThread()) {
163  if (m_operations[m_runningOperationID]->getSteppable()) {
164  enableWidgets(false, true, true, true);
165  } else {
166  enableWidgets(false, true, false, false);
167  }
168  } else {
169  enableWidgets(false, false, false, false);
170  }
171  break;
173  m_runningOperationID = -1;
174  currentOperationChanged(m_operationsList->currentIndex());
175  m_currentOperationInfo->setText("No running operation");
176  break;
178  m_stepOperationButton->setEnabled(true);
179  break;
181  m_stepOperationButton->setEnabled(false);
182  break;
184  // Blocking signals of the slider to avoid re-setting the value in the simulation
185  m_delaySlider->blockSignals(true);
186  m_delaySlider->setValue(delayToSliderValue(d->delay));
187  m_delaySlider->blockSignals(false);
188  break;
189  }
190  }
191 
192  return true;
193  } else {
194  return QWidget::event(e);
195  }
196 }
197 
198 void BaseExperimentGUI::currentOperationChanged(int index)
199 {
200  m_selectedOperationInfo->setText(describeOperation(((index < 0) ? index : (index + 1)), "Selected operation: "));
201 
202  if (m_runningOperationID == -1) {
203  if (index == -1) {
204  enableWidgets(false, false, false, false);
205  } else {
206  if (m_operations[index + 1]->getUseSeparateThread()) {
207  if (m_operations[index + 1]->getSteppable()) {
208  enableWidgets(true, false, true, true);
209  } else {
210  enableWidgets(true, false, false, false);
211  }
212  } else {
213  enableWidgets(true, false, false, false);
214  }
215  }
216  }
217 }
218 
219 void BaseExperimentGUI::startOperation()
220 {
221  if (m_operationsList->currentIndex() == -1) {
222  return;
223  }
224 
225  // Telling the experiment to start an operation
226  DatumToUpload d(*this);
227  if (m_pauseOperationButton->isChecked()) {
229  } else {
231  }
232  d->operationID = m_operationsList->currentIndex() + 1;
233 
234  m_startSelectedOperationButton->setEnabled(false);
235 }
236 
237 void BaseExperimentGUI::stopOperation()
238 {
239  // Telling the experiment to stop the current operation
240  DatumToUpload d(*this);
242 }
243 
244 void BaseExperimentGUI::pauseOperation(bool pause)
245 {
246  // Telling the experiment to pause/resume the current operation
247  DatumToUpload d(*this);
248 
249  if (pause) {
251  if (m_runningOperationID != -1) {
252  m_stepOperationButton->setEnabled(true);
253  }
254  } else {
256  m_stepOperationButton->setEnabled(false);
257  }
258 }
259 
260 void BaseExperimentGUI::stepOperation()
261 {
262  // Telling the experiment to perform a step
263  DatumToUpload d(*this);
265 }
266 
267 void BaseExperimentGUI::changeDelay(int delay)
268 {
269  // Telling the experiment the new delay
270  DatumToUpload d(*this);
272  d->interval = sliderValueToDelay(delay);
273 }
274 
275 void BaseExperimentGUI::updateOperationsList()
276 {
277  m_operations = m_experiment->getOperations();
278 
279  // First of all clearing the combobox
280  m_operationsList->clear();
281 
282  // Now adding all operations except the first one that is always stop
283  for (int i = 1; i < m_operations.size(); i++) {
284  m_operationsList->insertItem(i - 1, m_operations[i]->getName());
285  }
286 
287  // Selecting the first operation
288  m_operationsList->setCurrentIndex(0);
289 }
290 
291 void BaseExperimentGUI::enableWidgets(bool enableStart, bool enableStop, bool enablePause, bool enableDelay)
292 {
293  m_startSelectedOperationButton->setEnabled(enableStart);
294  m_stopCurrentOperationButton->setEnabled(enableStop);
295  m_pauseOperationButton->setEnabled(enablePause);
296  if ((enablePause) && (m_runningOperationID != -1)) {
297  m_stepOperationButton->setEnabled(m_pauseOperationButton->isChecked());
298  } else {
299  m_stepOperationButton->setEnabled(false);
300  }
301  m_delaySlider->setEnabled(enableDelay);
302 }
303 
304 QString BaseExperimentGUI::describeOperation(int id, QString prefix) const
305 {
306  QString str;
307 
308  if (id < 0) {
309  str = "Invalid operation";
310  } else {
311  str = prefix + m_operations[id]->getName();
312  if (m_operations[id]->getUseSeparateThread()) {
313  if (m_operations[id]->getSteppable()) {
314  str += "; it is a STEPPABLE THREAD operation";
315  } else {
316  str += "; it is a THREAD operation";
317  }
318  } else {
319  str += "; it is an IMMEDIATE operation";
320  }
321  }
322 
323  return str;
324 }
325 
326 unsigned long BaseExperimentGUI::sliderValueToDelay(int value) const
327 {
328  if (value == 0) {
329  return 0;
330  } else {
331  return (unsigned long) pow((double) 1.1, (double) value);
332  }
333 }
334 
335 int BaseExperimentGUI::delayToSliderValue(unsigned long interval) const
336 {
337  if (interval == 0) {
338  return 0;
339  } else {
340  return (int) (log((double) interval) / log((double) 1.1));
341  }
342 }
343 
344 } // End namespace farsa
345 
unsigned int operationID
The id of the operation that was added/started/ended...
unsigned long delay
The new delay for steps of the current operation.
unsigned long currentInterval() const
Returns the current delay for steppable operations.
virtual ~BaseExperimentGUI()
Destructor.
virtual bool event(QEvent *e)
Receives events.
const QVector< AbstractOperationWrapper * > & getOperations() const
Returns the list of operations.
DataUploaderDownloader< __BaseExperiment_internal::OperationStatus, __BaseExperiment_internal::OperationControl > * getUploaderDownloader()
Returns the object to exchange data.
The base class for experiments.
static void associate(DataUploader< DataType > *uploader, DataDownloader< DataType > *downloader)
BaseExperimentGUI(BaseExperiment *experiment, QWidget *parent=NULL, Qt::WindowFlags flags=0)
Constructor.
The data sent by a BaseExperiment instance to the corresponding GUI.