Newer
Older
Gigg, Martyn Anthony
committed
//---------------------------
// Includes
//--------------------------
#include "MantidQtCustomDialogs/CreateSampleShapeDialog.h"
#include "MantidQtAPI/AlgorithmInputHistory.h"
#include "MantidQtCustomDialogs/SampleShapeHelpers.h"
#include <QMenu>
#include <QLabel>
#include <QLineEdit>
#include <QComboBox>
#include <QMessageBox>
Gigg, Martyn Anthony
committed
#include <QShortcut>
#include <QCloseEvent>
Gigg, Martyn Anthony
committed
#include <iostream>
//Add this class to the list of specialised dialogs in this namespace
namespace MantidQt
{
namespace CustomDialogs
{
DECLARE_DIALOG(CreateSampleShapeDialog);
}
}
// Just to save writing this everywhere
using namespace MantidQt::CustomDialogs;
//---------------------------------------
// Public member functions
//---------------------------------------
/**
* Constructor
*/
CreateSampleShapeDialog::CreateSampleShapeDialog(QWidget *parent) :
Gigg, Martyn Anthony
committed
AlgorithmDialog(parent), m_setup_map(), m_details_map(), m_ops_map()
Gigg, Martyn Anthony
committed
{
}
/**
* Destructor
*/
CreateSampleShapeDialog::~CreateSampleShapeDialog()
{
Gigg, Martyn Anthony
committed
// Delete the objects created. The shape details static counter is decremented when
// its destructor is called
Gigg, Martyn Anthony
committed
QMutableMapIterator<BinaryTreeWidgetItem*, ShapeDetails*> itr(m_details_map);
Gigg, Martyn Anthony
committed
while( itr.hasNext() )
{
Gigg, Martyn Anthony
committed
itr.next();
ShapeDetails* obj = itr.value();
itr.remove();
delete obj;
Gigg, Martyn Anthony
committed
}
QMutableMapIterator<BinaryTreeWidgetItem*, Operation*> itrb(m_ops_map);
while( itrb.hasNext() )
{
itrb.next();
Operation *obj = itrb.value();
itrb.remove();
delete obj;
}
Gigg, Martyn Anthony
committed
}
/**
* Set up the dialog
*/
void CreateSampleShapeDialog::initLayout()
{
//The main setup function
m_uiForm.setupUi(this);
Gigg, Martyn Anthony
committed
m_setup_map.clear();
m_setup_map["sphere"] = new ShapeDetailsInstantiator<SphereDetails>;
m_setup_map["cylinder"] = new ShapeDetailsInstantiator<CylinderDetails>;
m_setup_map["infinite cylinder"] = new ShapeDetailsInstantiator<InfiniteCylinderDetails>();
Gigg, Martyn Anthony
committed
//The binary tree
m_shapeTree = new BinaryTreeWidget(this);
m_shapeTree->setColumnCount(1);
m_shapeTree->setHeaderLabel("");
m_shapeTree->setContextMenuPolicy(Qt::CustomContextMenu);
m_shapeTree->setSelectionBehavior(QAbstractItemView::SelectItems);
Gigg, Martyn Anthony
committed
m_shapeTree->setSelectionMode(QAbstractItemView::SingleSelection);
Gigg, Martyn Anthony
committed
connect(m_shapeTree, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(handleTreeContextMenuRequest(const QPoint &)));
connect(m_shapeTree, SIGNAL(itemSelectionChanged()), this, SLOT(setupDetailsBox()));
Gigg, Martyn Anthony
committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
connect(m_shapeTree, SIGNAL(treeDataChange(BinaryTreeWidgetItem*, int)), this,
SLOT(changeTreeData(BinaryTreeWidgetItem*, int)));
QPushButton *add_btn = new QPushButton("Add");
QMenu *add_menu = new QMenu(add_btn);
QMenu *add_op = new QMenu("Operation");
add_op->addAction(new QAction("intersection", add_op));
add_op->addAction(new QAction("union", add_op));
add_op->addAction(new QAction("difference", add_op));
connect(add_op, SIGNAL(triggered(QAction*)), this, SLOT(addOperation(QAction*)));
add_menu->addMenu(add_op);
QMenu *add_shape = new QMenu("Child Shape");
QStringList shapes = m_setup_map.keys();
QStringListIterator itr(shapes);
while( itr.hasNext() )
{
add_shape->addAction(new QAction(itr.next(), add_shape));
}
connect(add_shape, SIGNAL(triggered(QAction*)), this, SLOT(addShape(QAction*)));
add_menu->addMenu(add_shape);
add_btn->setMenu(add_menu);
QHBoxLayout *bottom = new QHBoxLayout;
bottom->addWidget(add_btn);
bottom->addStretch();
//Shape box layout
QVBoxLayout *shape_box_layout = new QVBoxLayout;
shape_box_layout->addWidget(m_shapeTree);
shape_box_layout->addLayout(bottom);
m_uiForm.shape_box->setLayout(shape_box_layout);
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
QShortcut *delete_key = new QShortcut(QKeySequence(Qt::Key_Delete), this);
connect(delete_key, SIGNAL(activated()), this, SLOT(handleDeleteRequest()));
// Check input workspace property. If there are available workspaces then
// these have been set as allowed values
std::vector<std::string> workspaces = getAlgorithmProperty("InputWorkspace")->allowedValues();
for( std::vector<std::string>::const_iterator itr = workspaces.begin(); itr != workspaces.end(); ++itr )
{
m_uiForm.wksp_opt->addItem(QString::fromStdString(*itr));
}
QLabel *validlbl = getValidatorMarker("InputWorkspace");
m_uiForm.bottomlayout->insertWidget(2, validlbl);
Gigg, Martyn Anthony
committed
}
/**
* Retrieve the input from the dialog
*/
void CreateSampleShapeDialog::parseInput()
{
Gigg, Martyn Anthony
committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
QString shapexml;
// First construct the XML that builds each separately defined shape
QMapIterator<BinaryTreeWidgetItem*, ShapeDetails*> detitr(m_details_map);
while( detitr.hasNext() )
{
detitr.next();
shapexml += detitr.value()->writeXML() + "\n";
}
QList<BinaryTreeWidgetItem*> postfix_exp;
//Build expression list
m_shapeTree->traverseInPostOrder(m_shapeTree->root(), postfix_exp);
QListIterator<BinaryTreeWidgetItem*> expitr(postfix_exp);
QStringList inter_results;
while( expitr.hasNext() )
{
BinaryTreeWidgetItem* item = expitr.next();
if( m_details_map.contains(item) )
{
inter_results.append(m_details_map.value(item)->getShapeID());
}
else if( m_ops_map.contains(item) )
{
int rcount = inter_results.count();
QString left = inter_results.at(rcount - 2);
QString right = inter_results.at(rcount - 1);
QString result = m_ops_map.value(item)->toString(left, right);
// Remove left result and replace the right with the result
inter_results.removeAt(rcount - 2);
//List has now been reduced in size by 1
inter_results.replace(rcount - 2, result);
}
else
{
shapexml = "";
break;
}
}
if( shapexml.isEmpty() ) return;
assert( inter_results.size() == 1 );
// std::cerr << inter_results.at(0).toStdString() << "\n";
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
shapexml += "<algebra val=\"" + inter_results.at(0) + "\" />";
addPropertyValueToMap("ShapeXML", shapexml);
// Get workspace value
addPropertyValueToMap("InputWorkspace", m_uiForm.wksp_opt->currentText());
Gigg, Martyn Anthony
committed
}
/**
* This slot is called when a context menu is requested inside the tree widget
*/
void CreateSampleShapeDialog::handleTreeContextMenuRequest(const QPoint & pos)
{
QMenu *context_menu = new QMenu(m_shapeTree);
//pos is in widget coordinates
Gigg, Martyn Anthony
committed
// QTreeWidgetItem *item = m_shapeTree->itemAt(pos);
// if( !item ) return;
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
QMenu *add_op = new QMenu("Add operation");
add_op->addAction(new QAction("intersection", add_op));
add_op->addAction(new QAction("union", add_op));
add_op->addAction(new QAction("difference", add_op));
connect(add_op, SIGNAL(triggered(QAction*)), this, SLOT(addOperation(QAction*)));
context_menu->addMenu(add_op);
Gigg, Martyn Anthony
committed
QMenu *submenu = new QMenu("Add child shape");
Gigg, Martyn Anthony
committed
QStringList shapes = m_setup_map.keys();
Gigg, Martyn Anthony
committed
QStringListIterator itr(shapes);
while( itr.hasNext() )
{
submenu->addAction(new QAction(itr.next(), submenu));
}
Gigg, Martyn Anthony
committed
connect(submenu, SIGNAL(triggered(QAction*)), this, SLOT(addShape(QAction*)));
Gigg, Martyn Anthony
committed
context_menu->addMenu(submenu);
Gigg, Martyn Anthony
committed
context_menu->addSeparator();
QAction *remove = new QAction("Delete", context_menu);
connect(remove, SIGNAL(triggered()), this, SLOT(handleDeleteRequest()));
context_menu->addAction(remove);
Gigg, Martyn Anthony
committed
context_menu->popup(QCursor::pos());
}
Gigg, Martyn Anthony
committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
void CreateSampleShapeDialog::changeTreeData(BinaryTreeWidgetItem* item, int data)
{
if( m_ops_map.contains(item) )
{
m_ops_map.value(item)->binaryop = data;
}
}
BinaryTreeWidgetItem* CreateSampleShapeDialog::getSelectedItem()
{
if( !m_shapeTree->selectedItems().isEmpty() )
{
/// Single selections are the only ones allowed
return dynamic_cast<BinaryTreeWidgetItem*>(m_shapeTree->selectedItems()[0]);
}
// Check if tree is empty
if( m_shapeTree->topLevelItemCount() == 0 )
{
// Give back the invisible root item
return m_shapeTree->root();
}
else
{
QMessageBox::information(this, "CreateSampleShape", "Please select an item in the list as a parent.");
}
return NULL;
}
Gigg, Martyn Anthony
committed
/**
* Add a new child shape
* @param shape The action that emitted the signal
*/
Gigg, Martyn Anthony
committed
void CreateSampleShapeDialog::addShape(QAction *shape)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
// Get the selected item
BinaryTreeWidgetItem *parent = getSelectedItem();
if( parent && parent->childCount() == 2 ) return;
Gigg, Martyn Anthony
committed
BinaryTreeWidgetItem *child = new BinaryTreeWidgetItem(QStringList(shape->text()));
Gigg, Martyn Anthony
committed
child->setFlags(child->flags() & ~Qt::ItemIsEditable);
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
if( m_shapeTree->topLevelItemCount() == 0 )
{
m_shapeTree->insertTopLevelItem(0, child);
}
else
{
parent->addChildItem(child);
}
// This calls setupDetails
m_shapeTree->setCurrentItem(child);
m_shapeTree->expandAll();
}
/**
* Add operation node based on menu action
*/
void CreateSampleShapeDialog::addOperation(QAction *opt)
{
//Get the selected item
BinaryTreeWidgetItem *parent = getSelectedItem();
if( parent && parent->childCount() == 2 ) return;
// if( !m_ops_map.contains(parent) )
// {
// QMessageBox::information(this, "CreateSampleShape", "An operation must be the child of an operation.");
// return;
// }
BinaryTreeWidgetItem *child = new BinaryTreeWidgetItem;
QFont font = child->font(0);
Gigg, Martyn Anthony
committed
font.setBold(true);
Gigg, Martyn Anthony
committed
child->setFont(0, font);
child->setData(0, Qt::DisplayRole, opt->text());
int opcode(0);
if( opt->text().startsWith("u") ) opcode = 1;
else if( opt->text().startsWith("d") ) opcode = 2;
else opcode = 0;
child->setData(0, Qt::UserRole, opcode);
child->setFlags(child->flags() | Qt::ItemIsEditable);
if( m_shapeTree->topLevelItemCount() == 0 )
{
m_shapeTree->insertTopLevelItem(0, child);
}
else
{
parent->addChildItem(child);
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
m_ops_map.insert(child, new Operation(opcode));
// This calls setupDetails if necessary
Gigg, Martyn Anthony
committed
m_shapeTree->setCurrentItem(child);
m_shapeTree->expandAll();
Gigg, Martyn Anthony
committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
}
/**
* Handle a delete signal
*/
void CreateSampleShapeDialog::handleDeleteRequest()
{
BinaryTreeWidgetItem *item = getSelectedItem();
if( !item ) return;
removeItem(item);
}
/**
* Remove an item and all children from the tree
*/
void CreateSampleShapeDialog::removeItem(BinaryTreeWidgetItem *item)
{
if( !item ) return;
//Recursively remove children
if( item->childCount() > 0 )
{
while( item->childCount() > 0 )
{
if( item->leftChild() ) removeItem(item->leftChild());
if( item->rightChild() ) removeItem(item->rightChild());
}
}
if( m_details_map.contains(item) )
{
ShapeDetails *obj = m_details_map.take(item);
delete obj;
}
else if( m_ops_map.contains(item) )
{
Operation *obj = m_ops_map.take(item);
delete obj;
}
else return;
if( item->parent() )
{
item->parent()->removeChild(item);
}
else
{
m_shapeTree->takeTopLevelItem(m_shapeTree->indexOfTopLevelItem(item));
}
Gigg, Martyn Anthony
committed
}
/**
* Setup the layout for the details box based upon the item given
*/
void CreateSampleShapeDialog::setupDetailsBox()
{
QList<QTreeWidgetItem*> selection = m_shapeTree->selectedItems();
if( selection.isEmpty() ) return;
// Remove the current widget if one exists in the scroll area
if( m_uiForm.details_scroll->widget() ) m_uiForm.details_scroll->takeWidget();
BinaryTreeWidgetItem *item = dynamic_cast<BinaryTreeWidgetItem*>(selection[0]);
QString shapename = item->text(0);
Gigg, Martyn Anthony
committed
if( m_setup_map.contains(shapename) )
Gigg, Martyn Anthony
committed
{
ShapeDetails *obj = NULL;
if( m_details_map.contains(item) )
{
obj = m_details_map.value(item);
}
else
{
Gigg, Martyn Anthony
committed
obj = createDetailsWidget(shapename);
Gigg, Martyn Anthony
committed
m_details_map.insert(item, obj);
}
//Set it as the currently displayed widget
m_uiForm.details_scroll->setWidget(obj);
}
}
/**
Gigg, Martyn Anthony
committed
* Create the correct type of details box for the shape
* @param shapename The name of the shape for which to create a widget
Gigg, Martyn Anthony
committed
* @return A pointer to the details object
*/
Gigg, Martyn Anthony
committed
ShapeDetails* CreateSampleShapeDialog::createDetailsWidget(const QString & shapename) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( m_setup_map.contains(shapename) )
{
return m_setup_map.value(shapename)->createInstance();
}
return NULL;
Gigg, Martyn Anthony
committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
}
//=================================================================
//=================================================================
//------------------------------------------------
// BinaryTreeWidgetItem
//------------------------------------------------
/**
* Default constructor
* @param type The type of the item
*/
BinaryTreeWidgetItem::BinaryTreeWidgetItem(int type)
: QTreeWidgetItem(type), m_left_index(0), m_right_index(1)
{
}
/**
* Construct an item with a string list of column texts to add
* @param A list of strings to appear as the column texts
* @param type Qt or User defined
*/
BinaryTreeWidgetItem::BinaryTreeWidgetItem(const QStringList & strings, int type)
: QTreeWidgetItem(strings, type), m_left_index(0), m_right_index(1)
{
}
/**
* Add a child item. This will only succeed if there are fewer than 2 children currently
*/
bool BinaryTreeWidgetItem::addChildItem(BinaryTreeWidgetItem* child)
{
if( childCount() >= 2 ) return false;
// Call sub-class function
this->addChild(child);
return true;
}
/**
* A pointer to the left child. It can be NULL
*/
BinaryTreeWidgetItem* BinaryTreeWidgetItem::leftChild() const
{
return dynamic_cast<BinaryTreeWidgetItem*>(this->child(m_left_index));
}
/**
* A pointer to the right child. It can be NULL
*/
BinaryTreeWidgetItem* BinaryTreeWidgetItem::rightChild() const
{
return dynamic_cast<BinaryTreeWidgetItem*>(this->child(m_right_index));
}
//------------------------------------------------
// BinaryTreeWidget
//------------------------------------------------
/**
* Default constructor
*/
BinaryTreeWidget::BinaryTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
ComboBoxDelegate *delegate = new ComboBoxDelegate(this);
setItemDelegate(delegate);
}
/**
* Gets the root item for the tree
*/
BinaryTreeWidgetItem* BinaryTreeWidget::root() const
{
return dynamic_cast<BinaryTreeWidgetItem*>(invisibleRootItem()->child(0));
}
/**
Gigg, Martyn Anthony
committed
* A recursive function that builds an expression from the binary tree by traversing it in a post order fashion
* @param node The parent node
* @param expression The expression list to build
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
void BinaryTreeWidget::traverseInPostOrder(BinaryTreeWidgetItem* node, QList<BinaryTreeWidgetItem*> & expression)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( !node ) return;
// For the time begin just print the string that we get
if( node->leftChild() ) traverseInPostOrder(node->leftChild(), expression);
if( node->rightChild() ) traverseInPostOrder(node->rightChild(), expression);
// Append this to the list
expression.append(node);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
void BinaryTreeWidget::dataChanged(const QModelIndex & topLeft, const QModelIndex &)
{
emit treeDataChange(dynamic_cast<BinaryTreeWidgetItem*>(itemFromIndex(topLeft)),
topLeft.data(Qt::UserRole).toInt());
}
Gigg, Martyn Anthony
committed
//------------------------------------------------
// ComboBoxDelegate
//------------------------------------------------
/**
* Default constructor
*/
ComboBoxDelegate::ComboBoxDelegate(QWidget *parent) : QItemDelegate(parent)
{
}
Gigg, Martyn Anthony
committed
/**
* Create an editor for a tree item
* @param parent The parent widget
*/
Gigg, Martyn Anthony
committed
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &,
const QModelIndex &) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItem("intersection");
Gigg, Martyn Anthony
committed
editor->addItem("union");
Gigg, Martyn Anthony
committed
editor->addItem("difference");
return editor;
}
Gigg, Martyn Anthony
committed
/**
* Set the data for the editor when it has been created
* @param editor The editor in question
* @param index The model item in question
*/
Gigg, Martyn Anthony
committed
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
Gigg, Martyn Anthony
committed
int value = index.model()->data(index, Qt::UserRole).toInt();
Gigg, Martyn Anthony
committed
QComboBox *combo_box = qobject_cast<QComboBox*>(editor);
combo_box->setCurrentIndex(value);
}
Gigg, Martyn Anthony
committed
/**
* Set the data for the model when editing is finished
* @param editor The editor in question
* @param model The model in question
* @param index The index for the model given
*/
Gigg, Martyn Anthony
committed
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *combo_box = static_cast<QComboBox*>(editor);
int boxitem = combo_box->currentIndex();
QString value = combo_box->itemText(boxitem);
Gigg, Martyn Anthony
committed
model->setData(index, boxitem, Qt::UserRole);
Gigg, Martyn Anthony
committed
model->setData(index, value, Qt::DisplayRole);
}
Gigg, Martyn Anthony
committed
/**
* Set the appropriate geometry for the widget
* @param editor The editor in question
* @param option The style option
*/
Gigg, Martyn Anthony
committed
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &) const
{
editor->setGeometry(option.rect);
}