Optika GUI Toolik  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Optika_treemodel.cpp
Go to the documentation of this file.
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Optika: A Tool For Developing Parameter Obtaining GUIs
5 // Copyright (2009) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, with Sandia Corporation, the
8 // U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Kurtis Nusbaum (klnusbaum@gmail.com)
38 //
39 // ***********************************************************************
40 // @HEADER
41 #include <QXmlStreamReader>
42 #include "Optika_treemodel.hpp"
45 #include <QTextStream>
46 #include <QDomElement>
47 
48 namespace Optika{
49 
50 
52  QString saveFileName, QObject *parent):
53  QAbstractItemModel(parent),
54  dependencies(nonnull(dependencySheet)),
55  validParameters(validParameters),
56  dependencySheet(dependencySheet)
57 {
58  basicSetup(saveFileName);
59  if(dependencies){
60  connect(this, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
61  this, SLOT(dataChangedListener(const QModelIndex&, const QModelIndex&)));
62  }
63 }
64 
66  delete rootItem;
67 }
68 
69 QVariant TreeModel::data(const QModelIndex &index, int role) const {
70  if(!index.isValid()){
71  return QVariant();
72  }
73  if(role != Qt::DisplayRole && role != Qt::ToolTipRole
74  && role != getRawDataRole())
75  {
76  return QVariant();
77  }
78  TreeItem *item = (TreeItem*)(index.internalPointer());
79  return item->data(index.column(), role);
80 }
81 
82 Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const {
83  if(!index.isValid()){
84  return Qt::ItemIsEnabled;
85  }
86  else if(index.column() == 1){
87  return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
88  }
89  else{
90  return QAbstractItemModel::flags(index);
91  }
92 }
93 
94 QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const{
95  if(orientation == Qt::Horizontal && role == Qt::DisplayRole){
96  return rootItem->data(section);
97  }
98  return QVariant();
99 }
100 
101 QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const{
102  if(!hasIndex(row, column, parent)){
103  return QModelIndex();
104  }
105  TreeItem *parentItem;
106 
107  if(!parent.isValid()){
108  parentItem = rootItem;
109  }
110  else{
111  parentItem = (TreeItem*)(parent.internalPointer());
112  }
113  TreeItem *childItem = parentItem->child(row);
114 
115  if(childItem){
116  return createIndex(row, column, childItem);
117  }
118  return QModelIndex();
119 }
120 
121 QModelIndex TreeModel::parent(const QModelIndex &index) const{
122  if(!index.isValid()){
123  return QModelIndex();
124  }
125 
126  TreeItem *childItem = (TreeItem*)(index.internalPointer());
127  TreeItem *parentItem = childItem->parent();
128 
129  if(parentItem == rootItem){
130  return QModelIndex();
131  }
132 
133  return createIndex(parentItem->row(), 0, parentItem);
134 }
135 
136 bool TreeModel::setData(const QModelIndex & index, const QVariant &value, int role){
137  if(index.isValid() && index.column() == 1 && role == Qt::EditRole){
138  TreeItem *item = (TreeItem*)(index.internalPointer());
139  if(item->changeValue(value)){
140  //Need to do this check because QDoubleValidators are really lax.
141  //Plus it's probably good to do anyway.
142  if(item->hasValidValue()){
143  emit dataChanged(index, index);
144  }
145  else{
146  emit badValue(index, item->getCurrentInvalidValueMessage());
147  }
148  }
149  return true;
150  }
151  return false;
152 }
153 
154 int TreeModel::rowCount(const QModelIndex &parent) const{
155  TreeItem *parentItem;
156  if(parent.column() > 0){
157  return 0;
158  }
159 
160  if (!parent.isValid()){
161  parentItem = rootItem;
162  }
163  else{
164  parentItem = (TreeItem*)(parent.internalPointer());
165  }
166 
167  return parentItem->childCount();
168 }
169 
170 int TreeModel::columnCount(const QModelIndex &parent) const {
171  if(parent.isValid()){
172  return ((TreeItem*)(parent.internalPointer()))->columnCount();
173  }
174  else{
175  return rootItem->columnCount();
176  }
177 }
178 
180  for(
181  DependencySheet::DepSet::const_iterator it =
182  dependencySheet->depBegin();
183  it != dependencySheet->depEnd();
184  ++it)
185  {
186  for(
187  Dependency::ConstParameterEntryList::const_iterator it2=
188  (*it)->getDependees().begin();
189  it2 != (*it)->getDependees().end();
190  ++it2)
191  {
192  QModelIndex dependeeIndex = findParameterEntryIndex(*it2);
193  TEUCHOS_TEST_FOR_EXCEPTION(!dependeeIndex.isValid(), std::logic_error,
194  "Could not find the index of the dependee. This is an internal error. "
195  "Please contact the Optika team.");
196  dataChangedListener(dependeeIndex, dependeeIndex);
197 
198  }
199  }
200 }
201 
202 void TreeModel::printOut() const{
203  rootItem->printOut();
204 }
205 
206 bool TreeModel::writeOutput(QString fileName){
207  QFile *file = new QFile(fileName);
208  if(!file->open(QIODevice::WriteOnly)){
209  return false;
210  }
211  std::ofstream outputFile;
213  XMLObject xmlOutput =
215  QTextStream outStream(file);
216  outStream << QString::fromStdString(xmlOutput.toString());
217  file->close();
218  delete file;
219  saved = true;
220  saveFileName = fileName;
221  return true;
222 }
223 
224 bool TreeModel::isRootIndex(const QModelIndex& index) const{
225  TreeItem* item = (TreeItem*)index.internalPointer();
226  return item == NULL;
227 }
228 
229 
231  const QDomElement& element,
232  const QModelIndex& potentialMatch) const
233 {
234  static QString nameAttr = QString::fromStdString(
236  if(isRootIndex(potentialMatch) && element.parentNode().isDocument()){
237  return true;
238  }
239  std::string potmatch = data(potentialMatch.sibling(potentialMatch.row(),0)).toString().toStdString();
240  std::string elemcont = element.attribute(nameAttr).toStdString();
241  if(data(potentialMatch.sibling(potentialMatch.row(),0)).toString() ==
242  element.attribute(nameAttr))
243  {
244  return isRealMatch(element.parentNode().toElement(), potentialMatch.parent());
245  }
246  return false;
247 
248 }
249 
250 void TreeModel::processInputElement(const QDomElement& element){
251  static QString nameAttrib = QString::fromStdString(
253  static QString valueAttrib = QString::fromStdString(
255  QDomNode n = element.firstChild();
256  while(!n.isNull()){
257  QDomElement e = n.toElement();
258  if(!e.isNull() && e.tagName().toStdString() == ParameterEntry::getTagName()){
259  QString name = e.attribute(nameAttrib, "");
260  TEUCHOS_TEST_FOR_EXCEPTION(name=="",std::runtime_error,
261  "Error: Found parameter with no name attribute. Check XML");
262  QList<QModelIndex> matches = match(index(0,0), Qt::DisplayRole, name,
263  -1, Qt::MatchExactly | Qt::MatchRecursive);
264  if(matches.size() !=0){
265  for(int i =0; i<matches.size(); ++i){
266  if(isRealMatch(e, matches[i])){
267  QModelIndex valueToEdit = matches.at(i).sibling(matches.at(i).row(), 1);
268  QString newValue = e.attribute(valueAttrib);
269  setData(valueToEdit,newValue, Qt::EditRole);
270  break;
271  }
272  }
273  }
274  }
275  else if(
276  !e.isNull()
277  &&
278  e.tagName().toStdString() == XMLParameterListWriter::getParameterListTagName()
279  )
280  {
282  }
283  n = n.nextSibling();
284  }
285 }
286 
287 void TreeModel::readInput(QString fileName){
288  QFile file(fileName);
289  TEUCHOS_TEST_FOR_EXCEPTION(!file.open(QIODevice::ReadOnly), std::runtime_error,
290  "Could not open file to read parameters.");
291  QDomDocument xmlDoc;
292  if(!xmlDoc.setContent(&file)){
293  file.close();
294  TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error,
295  "Error reading xml document. Bad XML syntax.");
296  }
297  file.close();
298  QDomElement docElem = xmlDoc.documentElement();
299  processInputElement(docElem);
300 }
301 
303  return saveFileName;
304 }
305 
307  return saved;
308 }
309 
311  delete rootItem;
312  QList<QVariant> headers;
313  rootItem = new TreeItem("", null, 0, true);
314  validParameters->setParameters(*canonicalList);
316  this->saveFileName = saveFileName;
317  if(saveFileName != ""){
318  saved = true;
320  }
321  else{
322  saved = false;
323  }
324  if(dependencies){
326  }
328 }
329 
330 QString TreeModel::itemType(const QModelIndex &index) const{
331  int row = index.row();
332  QModelIndex itemTypeIndex = index.sibling(row, 2);
333  return index.model()->data(itemTypeIndex, Qt::DisplayRole).toString();
334 }
335 
337  return dependencies;
338 }
339 
340 bool TreeModel::hasValidValue(QModelIndex valueToCheck) const{
341  TreeItem *item = static_cast<TreeItem*>(valueToCheck.internalPointer());
342  return item->hasValidValue();
343 }
344 
346  return itemEntry(index)->validator();
347 }
348 
350  return validParameters;
351 }
352 
353 QModelIndex TreeModel::parameterEntryMatch(const QModelIndex &start,
354  const RCP<const ParameterEntry> &parameterEntry) const
355 {
356  QModelIndex p = parent(start);
357  int from = start.row();
358  int to = rowCount(p);
359 
360  for (int r = from; r < to; ++r) {
361  QModelIndex idx = index(r, start.column(), p);
362  if(!idx.isValid())
363  continue;
365  if(entry != null && entry.get() == parameterEntry.get()){
366  return idx;
367  }
368 
369  if(hasChildren(idx)) { // search the hierarchy
370  QModelIndex childResult = parameterEntryMatch(index(0, idx.column(), idx), parameterEntry);
371  if(childResult.isValid()){
372  return childResult;
373  }
374  }
375  }
376  return QModelIndex();
377 }
378 
379 
381  RCP<const ParameterEntry> parameterEntry)
382 {
383  return parameterEntryMatch(index(0,0), parameterEntry);
384 }
385 
386 
388 TreeModel::itemEntry(const QModelIndex &index) const{
389  if(!index.isValid()){
390  return null;
391  }
392  TreeItem* item = (TreeItem*)index.internalPointer();
393  if(item->hasEntry()){
394  return item->getEntry();
395  }
396  else{
397  return null;
398  }
399 }
400 
402  for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
403  std::string name = parameterList->name(itr);
404  if(parameterList->isSublist(name)){
405  insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name, parentItem);
406  }
407  else if(parameterList->isParameter(name)){
408  insertParameter(parameterList->getEntryRCP(name), name, parentItem);
409  }
410  }
411 }
412 
414  std::string plname, TreeItem *parent)
415 {
416  QString truncatedName = QString::fromStdString(plname).section("->",-1);
417 
418 
419  TreeItem *newList = new TreeItem(truncatedName, listEntry, parent);
420  parent->appendChild(newList);
421  for(ParameterList::ConstIterator itr = parameterList->begin(); itr != parameterList->end(); ++itr){
422  std::string name = parameterList->name(itr);
423  if(parameterList->isSublist(name)){
424  insertParameterList(sublist(parameterList, name), parameterList->getEntryRCP(name), name, newList);
425  }
426  else if(parameterList->isParameter(name)){
427  insertParameter(parameterList->getEntryRCP(name), name, newList);
428  }
429  }
430 }
431 
432 void TreeModel::insertParameter(RCP<ParameterEntry> parameter, std::string name, TreeItem *parent){
433  parent->appendChild(new TreeItem(QString::fromStdString(name), parameter, parent));
434 }
435 
436 void TreeModel::basicSetup(QString saveFileName){
437  QList<QVariant> headers;
438  rootItem = new TreeItem("", null, 0, true);
441  this->saveFileName = saveFileName;
442  if(saveFileName != ""){
443  saved = true;
444  readInput(saveFileName);
445  }
446  else{
447  saved = false;
448  }
449 }
450 
451 void TreeModel::checkDependentState(const QModelIndex dependee, RCP<Dependency> dependency){
452  QModelIndex dependent;
453  Dependency::ParameterEntryList dependents= dependency->getDependents();
454  for(
455  Dependency::ParameterEntryList::iterator it = dependents.begin();
456  it != dependents.end();
457  ++it )
458  {
459  dependent = findParameterEntryIndex(*it);
460  if(!is_null(rcp_dynamic_cast<VisualDependency>(dependency))){
461  RCP<VisualDependency> visDep =
462  rcp_static_cast<VisualDependency>(dependency);
463  visDep->isDependentVisible() ?
464  emit showData(dependent.row(), dependent.parent()) :
465  emit hideData(dependent.row(), dependent.parent());
466  }
467 
468  if(!hasValidValue(dependent)){
469  QString message =
470  "Because you recently modified the " +
471  data(dependee, Qt::DisplayRole).toString() +
472  " parameter, the valid values for the " +
473  data(dependent, Qt::DisplayRole).toString() +
474  " parameter have changed.\n\nPlease modify the " +
475  data(dependent,Qt::DisplayRole).toString() + " value.\n";
476  emit badValue(dependent.sibling(dependent.row(), 1), message);
477  }
478  }
479 }
480 
482  saved = false;
483 }
484 
485 void TreeModel::dataChangedListener(const QModelIndex& index1, const QModelIndex& /*index2*/){
486  RCP<const ParameterEntry> changedIndexEntry =
487  itemEntry(index1);
488  QModelIndex dependee = index1.sibling(index1.row(), 0);
489  if(dependencySheet->hasDependents(changedIndexEntry)){
491  dependencySheet->getDependenciesForParameter(changedIndexEntry);
492  for(
493  DependencySheet::DepSet::const_iterator it = deps->begin();
494  it != deps->end();
495  ++it)
496  {
497  (*it)->evaluate();
498  checkDependentState(dependee,*it);
499  }
500  }
501 }
502 
503 
504 
505 }
506 
void badValue(QModelIndex badItem, QString message)
Emitted when it has been determined that a TreeItem no longer has a valid value.
RCP< DependencySheet > dependencySheet
The dependency sheet being used to determine any depdendencies between parameters.
void showData(int row, const QModelIndex &parent)
Emitted when a row should be shown.
bool saved
Whether or not the model has been saved since it was last modified.
XMLObject toXML(const ParameterList &p, RCP< const DependencySheet > depSheet=null) const
void appendChild(TreeItem *child)
Appends a child TreeItem to the TreeItem.
Qt::ItemFlags flags(const QModelIndex &index) const
bool is_null(const boost::shared_ptr< T > &p)
QVariant data(int column, int role=Qt::DisplayRole) const
Returns the data located in a particular column.
QModelIndex findParameterEntryIndex(RCP< const ParameterEntry > parameterEntry)
Finds the index of a particular parameter entry.
void reset()
Resets all the inputs to their default values.
std::string toString(const XMLObject &xml)
void currentFileNowModified()
When the state of any of the MainTree's items is changed, this slot should be called.
RCP< const ParameterList > canonicalList
A canonical list of what the validParameters were when they were first passed to the treemodel...
int childCount() const
Gets the number of child nodes this item has.
void insertParameterList(RCP< ParameterList > parameterList, RCP< ParameterEntry > listEntry, std::string plname, TreeItem *parent)
Inserts a new parameter list into the model.
TreeItem * rootItem
The root item of the model.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
std::set< RCP< ParameterEntry >, RCPComp > ParameterEntryList
static const std::string & getTagName()
bool changeValue(QVariant value)
Changes the value of the TreeItem. Should only be used with TreeItems that represent Parameters...
TreeModel(RCP< ParameterList > validParameters, RCP< DependencySheet > dependencySheet=null, QString saveFileName=QString(), QObject *parent=0)
Constructs the TreeModel.
QString itemType(const QModelIndex &index) const
Returns the type of item located at the specified QModelIndex.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
QString getCurrentInvalidValueMessage() const
Gets a message desribing the error with the current value.
bool hasValidValue(QModelIndex valueToCheck) const
Determines whether or not the value at the valueToCheck is valid.
QString getSaveFileName()
Gets the name of the save file with which the TreeModel is associated.
bool dependencies
Whether or not the model has any dependencies.
bool isRootIndex(const QModelIndex &index) const
T * get() const
RCP< const ParameterList > getCurrentParameters()
Get a ParameterList containing all of the parameters at their current settings.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
void printOut() const
Prints out the values in the TreeItem.
QString saveFileName
The name of the savefile associated with the model.
TreeItem * child(int row)
Returns the child treeitem in the row specified by the row argument.
QModelIndex parent(const QModelIndex &index) const
RCP< const ParameterEntry > itemEntry(const QModelIndex &index) const
Gets the ParameterEntry object given a QModelIndex.
void processInputElement(const QDomElement &element)
Given a Dom element, searches for the corresponding parameter in the model, updates it's value with t...
bool isRealMatch(const QDomElement &element, const QModelIndex &potentialMatch) const
Determines whether or not a model index corresponds to the parameter represented by the DomElement...
static const std::string & getValueAttributeName()
bool writeOutput(QString fileName)
Writes out the state of the current parameters in xml format.
void printOut() const
Prints out the model.
XMLParameterListWriter plWriter
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
RCP< const ParameterEntry > getEntry() const
Gets the ParameterEntry associated with this TreeItem.
void readInput(QString fileName)
Reads an xml file that describes the state of current parameters in xml format.
The TreeItem class is the item class used by the TreeModel class.
bool hasValidValue() const
Determines whether or not the current value associated with the TreeItem is valid.
RCP< const ParameterEntryValidator > getValidator(const QModelIndex &index) const
Gets the validator for a particular TreeItem.
~TreeModel()
Deconstructor for the TreeModel.
int rowCount(const QModelIndex &parent=QModelIndex()) const
int columnCount() const
How man columns the TreeItem has. Should always be 3.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
bool nonnull(const boost::shared_ptr< T > &p)
void hideData(int row, const QModelIndex &parent)
Emitted when a row should be hidden.
RCP< ParameterList > validParameters
The list of valid parameters.
void dataChangedListener(const QModelIndex &index1, const QModelIndex &index2)
Listens to see if any data has changed.
bool isSaved()
Determines wether or not the current state of TreeModel has been saved.
QModelIndex parameterEntryMatch(const QModelIndex &start, const RCP< const ParameterEntry > &parameterEntry) const
Finds the QModelIndex associated with a parameter entry.
void issueInitilizationSignals()
Issues any signals that need to be emitted right away.
bool hasEntry() const
Returns whether or not this TreeItem has a ParameterEntry associated with it.
void checkDependentState(const QModelIndex dependee, RCP< Dependency > dependency)
Checks the state of a dependent after it's dependency has been evaluated.
int row() const
Returns the row in which this TreeItem is located.
static const std::string & getParameterListTagName()
static const std::string & getNameAttributeName()
void basicSetup(QString saveFileName)
Basic setup shared by each of the constructors.
int n
bool hasDependencies()
Determines whether or not a Dependent Parameter List is being used in the TreeModel.
void readInParameterList(RCP< ParameterList > validParameters, TreeItem *parentItem)
Reads in the parameter list to be represented by the model.
TreeItem * parent()
Gets the parent TreeItem.
void insertParameter(RCP< ParameterEntry > parameter, std::string name, TreeItem *parent)
Inserts a new parameter into the model.
int columnCount(const QModelIndex &parent=QModelIndex()) const
static const int getRawDataRole()
Returns constant representing the RawDataRole.