Commit bf7b6e10 authored by Haocheng LIU's avatar Haocheng LIU
Browse files

FEATURE: Add active model support

SMTK&CMB now has a concept of active model. Loading, switching and
closing model(s)/data would reset the active model correspondingly.
A singleton class(qtActiveObjects) is used to store active model.
subPhraseGenerator would also keep a copy of it.
Only active model can be selected by rubber band. And only active model
would be expanded in model tree. Attribute panel would only list active
model's entities. Check CMB issue #140 for detail.
parent c5bb6818
......@@ -179,13 +179,13 @@ bool ModelEntityItem::appendValue(const smtk::model::EntityRef& val)
for (std::size_t i = 0; i < n; ++i)
{
if (this->isSet(i) && (this->value(i).entity() == val.entity()))
{
return true;
}
{
return true;
}
if (!this->isSet(i))
{
foundEmpty = true;
emptyIndex = i;
foundEmpty = true;
emptyIndex = i;
}
}
// If not, was there a space available?
......
......@@ -4,6 +4,7 @@ smtk_find_package_qt(qt_targets REQUIRED
# set up sources to build
set(QAttrLibSrcs
qtActiveObjects.cxx
qtUIManager.cxx
qtSelectionManager.cxx
qtAttribute.cxx
......@@ -59,6 +60,7 @@ set(QAttrLibUIs
set(QAttrLibMocHeaders
qtActiveObjects.h
qtUIManager.h
qtSelectionManager.h
qtAttribute.h
......
//=========================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#include <smtk/extension/qt/qtActiveObjects.h>
qtActiveObjects::qtActiveObjects()
{
this->m_activeModel = smtk::model::Model();
}
qtActiveObjects::~qtActiveObjects()
{
}
qtActiveObjects& qtActiveObjects::instance()
{
static qtActiveObjects theInstance;
return theInstance;
}
void qtActiveObjects::setActiveModel(const smtk::model::Model& inputModel)
{
this->m_activeModel = inputModel;
emit this->activeModelChanged();
}
//=========================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#ifndef qtActiveObjects_h
#define qtActiveObjects_h
#include <QObject>
#include "smtk/PublicPointerDefs.h"
#include "smtk/model/Model.h" //for smtk::model::Model
namespace smtk {
namespace model {
class Model;
}
}
class pqView;
class vtkSMSessionProxyManager;
class vtkSMViewProxy;
/**
qtActiveObjects keeps track of active objects.
This is similar to pqActiveObjects in ParaView, however it tracks objects
relevant to SMTK and CMB.
*/
class qtActiveObjects : public QObject
{
Q_OBJECT
typedef QObject Superclass;
public:
/// Returns reference to the singleton instance.
static qtActiveObjects& instance();
/// Returns the active model.
smtk::model::Model activeModel() const {return this->m_activeModel;}
public slots:
/// Set the active module.
void setActiveModel(const smtk::model::Model& inputModel);
signals:
/// Fire when activeModel changes.
void activeModelChanged();
private slots:
protected:
qtActiveObjects();
virtual ~qtActiveObjects();
smtk::model::Model m_activeModel;
private:
Q_DISABLE_COPY(qtActiveObjects)
};
#endif
......@@ -8,6 +8,7 @@
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#include "smtk/extension/qt/qtActiveObjects.h"
#include "smtk/extension/qt/qtAssociationWidget.h"
#include "smtk/extension/qt/qtAttribute.h"
......@@ -344,14 +345,14 @@ void qtAssociationWidget::showEntityAssociation(
typedef smtk::model::EntityRefs::const_iterator cit;
for (cit i =modelEnts.begin(); i != modelEnts.end(); ++i)
{
this->addModelAssociationListItem(this->Internals->CurrentList, *i, false);
this->addModelAssociationListItem(this->Internals->CurrentList, *i, false, true);
}
this->Internals->CurrentList->sortItems();
// The returned usedModelEnts should include all of the input modelEnts AND, if attDef is unique,
// entities that have been associated with the attributes with same definition.
std::set<smtk::model::EntityRef> usedModelEnts = this->processAttUniqueness(attDef, modelEnts);
// Now that we have add all the used model entities, we need to move on to all model
// Now that we have added all the used model entities, we need to move on to all model
// entities that are not associated with the attribute, but could be associated.
// We use the "no-exact match required" flag to catch any entity that could possibly match
// the association mask. This gets pruned below.
......@@ -366,18 +367,23 @@ void qtAssociationWidget::showEntityAssociation(
usedModelEnts.begin(), usedModelEnts.end(),
std::inserter(avail, avail.end()));
// Add a subset of the remainder to the list of available entities.
// Add a subset of the remainder which also belongs to current active model
// to the list of available entities.
// We create a temporary group and use Group::meetsMembershipConstraints()
// to test whether the mask allows association.
smtk::model::Model activeModel = qtActiveObjects::instance().activeModel();
smtk::model::Manager::Ptr tmpMgr = smtk::model::Manager::create();
Group tmpGrp = tmpMgr->addGroup();
tmpGrp.setMembershipMask(attDef->associationMask());
for(EntityRefs::iterator i = avail.begin(); i != avail.end(); ++i)
{
if (tmpGrp.meetsMembershipConstraints(*i))
if (tmpGrp.meetsMembershipConstraints(*i) &&
i->owningModel().entity() == activeModel.entity())
{
this->addModelAssociationListItem(this->Internals->AvailableList, *i, false);
}
}
this->Internals->AvailableList->sortItems();
this->Internals->CurrentList->blockSignals(false);
this->Internals->AvailableList->blockSignals(false);
......@@ -550,10 +556,13 @@ void qtAssociationWidget::removeItem(QListWidget* theList,
}
QListWidgetItem* qtAssociationWidget::addModelAssociationListItem(
QListWidget* theList, smtk::model::EntityRef modelItem, bool sort)
QListWidget* theList, smtk::model::EntityRef modelItem, bool sort, bool
appendModelName)
{
std::string name = appendModelName ? (modelItem.name() +
" - " + modelItem.owningModel().name()) : modelItem.name();
QListWidgetItem* item = new QListWidgetItem(
QString::fromStdString(modelItem.name()),
QString::fromStdString(name),
theList,
smtk_USER_DATA_TYPE);
//save the entity as a uuid string
......@@ -648,8 +657,14 @@ void qtAssociationWidget::onRemoveAssigned()
{
this->Internals->CurrentAtt.lock()->disassociateEntity(currentItem);
this->removeItem(theList, item);
selItem = this->addModelAssociationListItem(
this->Internals->AvailableList, currentItem);
// only add entityRef back to availableList when it belongs to
// the current active model
if (currentItem.owningModel().entity() ==
qtActiveObjects::instance().activeModel().entity())
{
selItem = this->addModelAssociationListItem(
this->Internals->AvailableList, currentItem);
}
}
}
else if(!this->Internals->CurrentModelGroup.isNull())
......@@ -695,7 +710,7 @@ void qtAssociationWidget::onAddAvailable()
{
this->removeItem(theList, item);
selItem = this->addModelAssociationListItem(
this->Internals->CurrentList, currentItem);
this->Internals->CurrentList, currentItem, true, true);
}
else // faied to assoicate with new entity
{
......@@ -773,11 +788,16 @@ void qtAssociationWidget::onExchange()
{
this->removeItem(this->Internals->CurrentList, selCurrentItems[i]);
selCurrentItem = this->addModelAssociationListItem(
this->Internals->CurrentList, availableItem);
this->Internals->CurrentList, availableItem, true, true);
this->removeItem(this->Internals->AvailableList, selAvailItems[i]);
selAvailItem = this->addModelAssociationListItem(
this->Internals->AvailableList, currentItem);
// only add it back to available list when it's part of active model
if (currentItem.owningModel().entity() ==
qtActiveObjects::instance().activeModel().entity())
{
selAvailItem = this->addModelAssociationListItem(
this->Internals->AvailableList, currentItem);
}
}
else // faied to exchange, add back association
{
......
......@@ -74,8 +74,10 @@ namespace smtk
//returns the Item it has added to the widget
//ownership of the item is handled by the widget so no need to delete
//for now we append model name to currentList
virtual QListWidgetItem* addModelAssociationListItem(
QListWidget* theList, smtk::model::EntityRef modelItem, bool sort=true);
QListWidget* theList, smtk::model::EntityRef modelItem,
bool sort=true, bool appendModelName = false);
//returns the Item it has added to the widget
//ownership of the item is handled by the widget so no need to delete
......
......@@ -15,6 +15,7 @@
#include "smtk/attribute/ModelEntityItem.h"
#include "smtk/attribute/ModelEntityItemDefinition.h"
#include "smtk/attribute/System.h"
#include "smtk/extension/qt/qtActiveObjects.h"
#include "smtk/extension/qt/qtMeshItem.h"
#include "smtk/extension/qt/qtModelEntityItem.h"
#include "smtk/mesh/Collection.h"
......@@ -151,7 +152,10 @@ void qtModelEntityItemCombo::init()
// if the mask is only groups, get all groups from manager
((onlyGroups && entref.isGroup()) ||
// else, check the membership constraints
(!onlyGroups && tmpGrp.meetsMembershipConstraints(entref))))
(!onlyGroups && tmpGrp.meetsMembershipConstraints(entref))) &&
// only show entities in active model or its type is model
(entref.owningModel().entity() ==
qtActiveObjects::instance().activeModel().entity() || entref.isModel()))
{
QStandardItem* item = new QStandardItem;
std::string entName = entref.name();
......
......@@ -138,7 +138,13 @@ void QEntityItemDelegate::paint(
QFont titleFont = QApplication::font();
QFont subtitleFont = QApplication::font();
titleFont.setPixelSize(this->m_titleFontSize);
/// add a method to set/get title font
titleFont.setBold(this->titleFontWeight() > 1 ? true : false);
// bold the active model title
if (idx.data(Qt::FontRole).toBool())
{
titleFont.setBold(true);
}
subtitleFont.setPixelSize(this->m_subtitleFontSize);
subtitleFont.setBold(this->subtitleFontWeight() > 1 ? true : false);
......
......@@ -9,6 +9,7 @@
//=========================================================================
#include "smtk/extension/qt/qtEntityItemModel.h"
#include "smtk/extension/qt/qtActiveObjects.h"
#include "smtk/model/Entity.h"
#include "smtk/model/EntityPhrase.h"
#include "smtk/model/FloatData.h"
......@@ -356,7 +357,7 @@ QVariant QEntityItemModel::data(const QModelIndex& idx, int role) const
}
else if (role == EntityColorRole &&
(item->phraseType() == MESH_SUMMARY || item->relatedEntity().isValid()
|| item->phraseType() == ENTITY_LIST )) // if needed, disable assign model color here
|| item->phraseType() == ENTITY_LIST ))
{
QColor color;
FloatList rgba = item->relatedColor();
......@@ -390,6 +391,14 @@ QVariant QEntityItemModel::data(const QModelIndex& idx, int role) const
}
return color;
}
else if (role == Qt::FontRole)
{
// used to bold the active model's title
return (item->relatedEntity().entity() ==
qtActiveObjects::instance().activeModel().entity()) ? QVariant(true)
: QVariant(false);
}
}
}
return QVariant();
......@@ -1454,6 +1463,10 @@ void QEntityItemModel::newSessionOperatorResult(
smtk::model::DescriptivePhrases newSubs =
this->m_root->findDelegate()->subphrases(this->m_root);
// udpate active model in subphraseGenerator
this->m_root->findDelegate()->setActiveModel(qtActiveObjects::instance().activeModel());
std::vector< std::pair<DescriptivePhrasePtr, int> > newDphrs;
int newIdx = 0;
for (smtk::model::DescriptivePhrases::iterator it = newSubs.begin();
......
......@@ -9,6 +9,7 @@
//=========================================================================
#include "smtk/extension/qt/qtModelPanel.h"
#include "smtk/extension/qt/qtActiveObjects.h"
#include "smtk/extension/qt/qtEntityItemModel.h"
#include "smtk/extension/qt/qtModelView.h"
#include "smtk/model/Entity.h"
......@@ -112,6 +113,7 @@ void qtModelPanel::resetView(qtModelPanel::enumTreeView enType,
spg->setDirectLimit(-1);
spg->setSkipAttributes(true);
spg->setSkipProperties(true);
spg->setActiveModel(qtActiveObjects::instance().activeModel());
qtModelView* modelview = this->getModelView();
QPointer<smtk::extension::QEntityItemModel> qmodel = modelview->getModel();
......
......@@ -8,15 +8,19 @@
// PURPOSE. See the above copyright notice for more information.
//=========================================================================
#include "smtk/extension/qt/qtModelView.h"
#include "smtk/extension/qt/qtActiveObjects.h"
#include "smtk/model/DescriptivePhrase.h"
#include "smtk/model/Entity.h"
#include "smtk/model/EntityIterator.h"
#include "smtk/model/FloatData.h"
#include "smtk/model/Group.h"
#include "smtk/model/IntegerData.h"
#include "smtk/model/Manager.h"
#include "smtk/model/Model.h"
#include "smtk/model/StringData.h"
#include "smtk/model/SubphraseGenerator.h"
#include "smtk/model/SimpleModelSubphrases.h"
#include "smtk/extension/qt/qtEntityItemDelegate.h"
......@@ -38,6 +42,7 @@
#include "smtk/extension/qt/qtAttribute.h"
#include "smtk/extension/qt/qtModelEntityItem.h"
#include "smtk/extension/qt/qtModelPanel.h"
#include "smtk/extension/qt/qtModelOperationWidget.h"
#include "smtk/extension/qt/qtOperatorDockWidget.h"
#include "smtk/extension/qt/qtUIManager.h"
......@@ -385,7 +390,7 @@ void qtModelView::selectionChanged (
emit this->sendSelectionsFromModelViewToSelectionManager(selentityrefs,
selmeshes, selproperties,
smtk::extension::SelectionModifier::SELECTION_REPLACE_UNFILTERED,
smtk::model::StringList());
skipList);
}
// when the dataChanged is emitted from the model, we want to scroll to
......@@ -395,6 +400,119 @@ void qtModelView::newIndexAdded(const QModelIndex & newidx)
this->scrollTo(newidx);
}
void qtModelView::filterSelectionByEntity (const smtk::model::DescriptivePhrasePtr& dPhrase,
smtk::model::EntityRefs& selentityrefs,
smtk::mesh::MeshSets* selmeshes)
{
SubphraseGeneratorPtr gen = dPhrase->findDelegate();
SimpleModelSubphrasesPtr sGen = smtk::dynamic_pointer_cast<SimpleModelSubphrases>(gen);
qtModelPanel::enumTreeView enType = sGen ? qtModelPanel::VIEW_BY_TOPOLOGY
: qtModelPanel::VIEW_BY_ENTITY_LIST;
BitFlags defaultMask = CELL_ENTITY | GROUP_ENTITY | MODEL_ENTITY
| AUX_GEOM_ENTITY | INSTANCE_ENTITY | SESSION;
smtk::model::EntityRef relatedEnt = dPhrase->relatedEntity();
if(dPhrase)
{
if(dPhrase->phraseType() == MESH_SUMMARY ||
dPhrase->phraseType() == MESH_LIST)
{
this->selectMeshes(dPhrase, selmeshes);
}
else
{// single entity
BitFlags masked = dPhrase->relatedEntity().entityFlags() & defaultMask;
if (masked)
{
selentityrefs.insert(relatedEnt);
}
}
if (relatedEnt.entityFlags() == SESSION)
{// session condition
smtk::model::SessionRef sessionRef = relatedEnt.
as<smtk::model::SessionRef>();
// loop through each model, its entities and meshes
Models models = sessionRef.models<Models>();
for (const auto& model : models)
{
smtk::model::EntityIterator eit;
eit.traverse(model, smtk::model::ITERATE_MODELS);
for (eit.begin(); !eit.isAtEnd(); eit.advance())
{
// needed? @David
if (eit->isCellEntity() || eit->isAuxiliaryGeometry() || eit->isModel())
{
selentityrefs.insert(*eit);
}
}
smtk::mesh::ManagerPtr meshMgr = model.manager()->meshes();
std::vector<smtk::mesh::CollectionPtr> meshCols = meshMgr->
associatedCollections(model);
for (const auto & meshCol : meshCols)
{
selmeshes->insert(meshCol->meshes());
}
}
}
else if (relatedEnt.isModel())
{// non-active model and active model
smtk::model::EntityIterator eit;
eit.traverse(relatedEnt, smtk::model::ITERATE_CHILDREN);
for (eit.begin(); !eit.isAtEnd(); eit.advance())
{
if (eit->isCellEntity() || eit->isAuxiliaryGeometry() || eit->isModel())
{
selentityrefs.insert(*eit);
}
}
smtk::mesh::ManagerPtr meshMgr = relatedEnt.manager()->meshes();
std::vector<smtk::mesh::CollectionPtr> meshCols = meshMgr->
associatedCollections(relatedEnt);
for (const auto & meshCol : meshCols)
{
selmeshes->insert(meshCol->meshes());
}
}
else if (dPhrase->phraseType() == ENTITY_LIST)
{// handle items in ENTITY_LIST
if (enType == qtModelPanel::VIEW_BY_ENTITY_LIST)
{
if(EntityListPhrasePtr elist = smtk::dynamic_pointer_cast<EntityListPhrase>
(dPhrase))
{
smtk::model::EntityRefArray entities = elist->relatedEntities();
for (const auto& item: entities)
{
selentityrefs.insert(item);
}
}
}
}
else if (dPhrase->phraseType() == ENTITY_SUMMARY)
{
if (enType == qtModelPanel::VIEW_BY_TOPOLOGY)
{//VIEW_BY_TOPOLOGY
if(EntityPhrasePtr entity = smtk::dynamic_pointer_cast<EntityPhrase>
(dPhrase))
{
smtk::model::EntityRef ent = entity->relatedEntity();
EntityRefs lowerEnts = ent.lowerDimensionalBoundaries(-1);
for (const auto& lowerEnt: lowerEnts)
{
selentityrefs.insert(lowerEnt);
}
// EntityIterator would fail here to give wrong boundary entities
}
}
}
}
}
void qtModelView::recursiveSelect (const smtk::model::DescriptivePhrasePtr& dPhrase,
smtk::model::EntityRefs& selentityrefs, BitFlags entityFlags,
smtk::model::DescriptivePhrases& selproperties, bool exactMatch,
......@@ -507,6 +625,15 @@ void qtModelView::currentSelectionByMask (
}
}
void qtModelView::updateActiveModelByModelIndex()
{
// get the current model and compare with active model
smtk::model::Model currentModel = this->owningEntityAs<smtk::model::Model>(
this->m_contextMenuIndex);
qtActiveObjects::instance().setActiveModel(currentModel);
}
bool qtModelView::removeSession(const smtk::model::SessionRef& sref)
{
smtk::extension::QEntityItemModel* qmodel =
......@@ -773,6 +900,9 @@ void qtModelView::showContextMenu(const QModelIndex &idx, const QPoint& p)
smtk::model::SessionRef brSession =
this->owningEntityAs<smtk::model::SessionRef>(idx);
// get the current model and compare with active model
smtk::model::Model currentModel = this->owningEntityAs<smtk::model::Model>(idx);
if (!brSession.isValid())
{
// Nothing to do the session is not valid;
......@@ -796,14 +926,32 @@ void qtModelView::showContextMenu(const QModelIndex &idx, const QPoint& p)
std::pair<std::vector<std::string>, std::map<std::string, std::string> >(keyList, opLabelsMap);
}
auto sinfo = this->m_sessionInfo[sessionString];
for(StringList::const_iterator it = sinfo.first.begin();
it != sinfo.first.end(); ++it)
{
QAction* act = this->m_ContextMenu->addAction((*it).c_str());
// Compare the current model with active model. If true, show related
// operators. If not, only show `set as active model`.
smtk::model::Model currentActiveModel = qtActiveObjects::instance().activeModel();
if (currentModel.isValid() && (currentModel.entity() == currentActiveModel.entity()))
{
for(StringList::const_iterator it = sinfo.first.begin();
it != sinfo.first.end(); ++it)
{
QAction* act = this->m_ContextMenu->addAction((*it).c_str());
QVariant vdata( QString::fromStdString(sessionString) );
act->setData(vdata);
QObject::connect(act, SIGNAL(triggered()), this, SLOT(operatorInvoked()));
}
}
else if (currentModel.isValid()) // if invalid, then it's sessionRef
{ // set active model
std::string setAsActiveModel("set as active model");
QAction* act = this->m_ContextMenu->addAction(setAsActiveModel.c_str());
QVariant vdata( QString::fromStdString(sessionString) );
act->setData(vdata);
QObject::connect(act, SIGNAL(triggered()), this, SLOT(operatorInvoked()));
}
QObject::connect(act, SIGNAL(triggered()), this, SLOT(updateActiveModelByModelIndex()));
}
// store current contextMenuIndex to set active model
this->m_contextMenuIndex = idx;
QPoint popP = p;
if(popP.isNull())
{
......@@ -1125,11 +1273,8 @@ void qtModelView::toggleEntityVisibility( const QModelIndex& idx)
smtk::model::EntityRefs selentityrefs;
smtk::mesh::MeshSets selmeshes;
smtk::model::DescriptivePhrases selproperties;
this->recursiveSelect(dp, selentityrefs,
CELL_ENTITY | SHELL_ENTITY | GROUP_ENTITY |
MODEL_ENTITY | AUX_GEOM_ENTITY | INSTANCE_ENTITY | SESSION,
selproperties, false, &selmeshes);
// filter selection by entity instead of DesriptivePhrase
this->filterSelectionByEntity(dp, selentityrefs,&selmeshes);
bool visible = true;
if(dp->phraseType() == MESH_LIST || dp->phraseType() == MESH_SUMMARY)
{
......@@ -1590,6 +1735,10 @@ void qtModelView::updateWithOperatorResult(
{
smtk::extension::QEntityItemModel* qmodel =
dynamic_cast<smtk::extension::QEntityItemModel*>(this->model());
// udpate active model in subphraseGenerator. Get the m_root
qmodel->getItem(QModelIndex())->findDelegate()->
setActiveModel(qtActiveObjects::instance().activeModel());
QModelIndex top = this->rootIndex();
for (int row = 0; row < qmodel->rowCount(top); ++row)
{
......
......@@ -80,6 +80,7 @@ namespace smtk{
void expandAllModels();
public slots:
void updateActiveModelByModelIndex();
bool removeSession(const smtk::model::SessionRef& sref);