Commit c28241f3 authored by David Thompson's avatar David Thompson

Propose a `configure()` method for operations.

parent e6fe60bc
Pipeline #129711 passed with stage
## Operation system changes
### Configuration
Operations now have a method named `configure()`.
This method may be invoked by a user interface when
the operation's associations are changed, item values are
changed, or new attributes are created (in the operation's
resource) in order for the operation to edit itself
for consistency and to provide context-sensitive default
values for items.
An example is provided in the oscillator-session's EditSource
operation that uses changes to associations to compute a default
center and radius for the source.
......@@ -128,6 +128,20 @@ std::size_t ReferenceItem::maxNumberOfValues() const
return def->maxNumberOfValues();
}
bool ReferenceItem::contains(const smtk::resource::PersistentObjectPtr& obj) const
{
bool doesContain = false;
this->visit([&](PersistentObjectPtr other) {
if (other == obj)
{
doesContain = true;
return false; // stop iterating
}
return true; // keep iterating
});
return doesContain;
}
void ReferenceItem::visit(std::function<bool(PersistentObjectPtr)> visitor) const
{
for (auto it = this->begin(); it != this->end(); ++it)
......
......@@ -85,6 +85,9 @@ public:
/// Return the maximum number of values allowed by this item's definition (or 0).
std::size_t maxNumberOfValues() const;
/// Return true if the ReferenceItem contains a reference to the given object.
bool contains(const smtk::resource::PersistentObjectPtr& obj) const;
/**\brief Invoke a method on each value of this item.
*
* If the lambda returns false, iteration will terminate immediately.
......
......@@ -5,10 +5,10 @@
<!-- Operation -->
<include href="smtk/operation/Operation.xml"/>
<AttDef Type="mark modified"
Label="Resource - Mark as Modified" BaseType="operation">
<AttDef Type="signal"
Label="Attribute - Signal Changes" BaseType="operation">
<BriefDescription>
Mark the specified components as created, modified, or expunged.
Indicate that an attribute was created, modified, or expunged.
</BriefDescription>
<ItemDefinitions>
<Component Name="created" Extensible="true" NumberOfRequiredValues="0">
......@@ -25,7 +25,7 @@
<!-- Result -->
<include href="smtk/operation/Result.xml"/>
<AttDef Type="result(mark modified)" BaseType="result"/>
<AttDef Type="result(signal)" BaseType="result"/>
</Definitions>
</SMTK_AttributeResource>
......@@ -376,12 +376,25 @@ void pqSMTKOperationPanel::operationListDoubleClicked(QListWidgetItem* item)
auto seln = m_availableOperations->selection();
const auto& smap = seln->currentSelection();
auto params = opInstance->parameters();
bool anyAssociations = false;
for (auto entry : smap)
{
if ((entry.second & 0x01) ==
0x01) // FIXME: properly select entities from the map based on a specific bit flag
{
params->associate(entry.first);
anyAssociations = true;
}
}
if (anyAssociations)
{
if (opInstance->configure(nullptr, params->associations()))
{
auto baseView = m_attrUIMgr ? m_attrUIMgr->topView() : nullptr;
if (baseView)
{
baseView->attributeChanged(opInstance->parameters());
}
}
}
if (this->editOperation(opInstance))
......
......@@ -13,9 +13,13 @@
#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/ValueItem.h"
#include "smtk/attribute/ValueItemDefinition.h"
#include "smtk/attribute/operators/Signal.h"
#include "smtk/model/EntityRef.h"
#include "smtk/operation/Manager.h"
#include "smtk/operation/Observer.h"
#include "smtk/io/Logger.h"
#include "pqActiveObjects.h"
......@@ -170,6 +174,17 @@ pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(
m_p->m_overrideWhen = pqSMTKAttributeItemWidget::OverrideWhenConvert(ow);
m_p->m_fallbackStrategy = pqSMTKAttributeItemWidget::FallbackStrategyConvert(fs);
m_p->m_geometrySource = pqSMTKAttributeItemWidget::GeometrySourceConvert(gs);
m_p->m_opObserver = info.baseView()->uiManager()->operationManager()->observers().insert(
[&](std::shared_ptr<smtk::operation::Operation> op, smtk::operation::EventType event,
smtk::operation::Operation::Result res) {
if (event == smtk::operation::EventType::DID_OPERATE &&
std::dynamic_pointer_cast<smtk::attribute::Signal>(op) &&
res->findReference("modified")->contains(this->item()->attribute()))
{
this->updateWidgetFromItem();
}
return 0;
});
}
pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(smtk::attribute::ItemPtr itm, QWidget* p,
......@@ -177,12 +192,29 @@ pqSMTKAttributeItemWidget::pqSMTKAttributeItemWidget(smtk::attribute::ItemPtr it
: qtItem(smtk::extension::AttributeItemInfo(itm, smtk::view::View::Component(), p, bview))
{
m_p = new Internal(itm, this->widget(), bview, orient);
m_p->m_opObserver = bview->uiManager()->operationManager()->observers().insert(
[&](std::shared_ptr<smtk::operation::Operation> op, smtk::operation::EventType event,
smtk::operation::Operation::Result res) {
if (event == smtk::operation::EventType::DID_OPERATE &&
std::dynamic_pointer_cast<smtk::attribute::Signal>(op) &&
res->findReference("modified")->contains(this->item()->attribute()))
{
this->updateWidgetFromItem();
}
return 0;
});
m_isLeafItem = true;
this->createWidget();
}
pqSMTKAttributeItemWidget::~pqSMTKAttributeItemWidget()
{
auto ui = this->uiManager();
auto operationManager = ui ? ui->operationManager() : nullptr;
if (operationManager && m_p->m_opObserver >= 0)
{
operationManager->observers().erase(m_p->m_opObserver);
}
delete this->m_p;
this->m_p = NULL;
}
......
......@@ -87,6 +87,7 @@ public:
public slots:
virtual void updateItemFromWidget() = 0;
virtual void updateWidgetFromItem() {}
/**\brief Change whether the item is enabled (and thus the widget active).
*
......
......@@ -39,6 +39,7 @@ public:
, m_overrideWhen(OverrideWhen::Unset)
, m_geometrySource(GeometrySource::BestGuess)
, m_fallbackStrategy(FallbackStrategy::Hide)
, m_opObserver(-1)
{
(void)itm;
(void)p;
......@@ -58,4 +59,6 @@ public:
// state of children
QMap<QWidget*, QPair<QLayout*, QWidget*> > m_children;
int m_opObserver;
};
......@@ -122,6 +122,32 @@ void pqSMTKSphereItemWidget::updateItemFromWidget()
}
}
/// Retrieve property values from ParaView proxy and store them in the attribute's Item.
void pqSMTKSphereItemWidget::updateWidgetFromItem()
{
smtk::attribute::DoubleItemPtr centerItem;
smtk::attribute::DoubleItemPtr radiusItem;
if (!fetchCenterAndRadiusItems(centerItem, radiusItem))
{
return;
}
vtkSMNewWidgetRepresentationProxy* widget = m_p->m_pvwidget->widgetProxy();
// pqImplicitPlanePropertyWidget* pw = dynamic_cast<pqImplicitPlanePropertyWidget*>(m_p->m_pvwidget);
vtkSMPropertyHelper centerHelper(widget, "Center");
vtkSMPropertyHelper radiusHelper(widget, "Radius");
bool didChange = false;
for (int i = 0; i < 3; ++i)
{
double cv = centerItem->value(i);
didChange |= (centerHelper.GetAsDouble(i) != cv);
centerHelper.Set(i, cv);
}
double rv = radiusItem->value(0);
didChange |= (radiusHelper.GetAsDouble(0) != rv);
radiusHelper.Set(rv);
}
bool pqSMTKSphereItemWidget::fetchCenterAndRadiusItems(
smtk::attribute::DoubleItemPtr& centerItem, smtk::attribute::DoubleItemPtr& radiusItem)
{
......
......@@ -32,6 +32,7 @@ public:
static qtItem* createSphereItemWidget(const AttributeItemInfo& info);
bool createProxyAndWidget(vtkSMProxy*& proxy, pqInteractivePropertyWidget*& widget) override;
void updateItemFromWidget() override;
void updateWidgetFromItem() override;
protected:
/**\brief Starting with the widget's assigned item (which must
......
......@@ -273,6 +273,7 @@ void qtAttribute::onItemModified()
static_cast<int>(smtk::operation::Operation::Outcome::SUCCEEDED);
}
}
#ifndef NDEBUG
if (!didNotify)
{
static bool once = true;
......@@ -280,9 +281,13 @@ void qtAttribute::onItemModified()
{
once = false;
smtkWarningMacro(smtk::io::Logger::instance(),
"Could not notify resource observers that resource state changed.");
"Could not notify resource observers that resource state changed. "
"This is not necessarily an error if the operation is unmanaged.");
}
}
#else
(void)didNotify;
#endif // NDEBUG
}
emit this->itemModified(iobject);
emit this->modified();
......
......@@ -30,7 +30,11 @@ using namespace smtk::extension;
class qtInstancedViewInternals
{
public:
qtInstancedViewInternals() { m_isEmpty = true; }
qtInstancedViewInternals()
: m_isEmpty(true)
{
}
//QScrollArea *ScrollArea;
QList<QPointer<qtAttribute> > AttInstances;
bool m_isEmpty;
......@@ -176,6 +180,8 @@ void qtInstancedView::updateAttributeData()
this->Internals->m_isEmpty = false;
}
QObject::connect(attInstance, SIGNAL(modified()), this, SIGNAL(modified()));
QObject::connect(
attInstance, SIGNAL(itemModified(qtItem*)), this, SIGNAL(itemModified(qtItem*)));
}
}
}
......
......@@ -46,6 +46,7 @@ public slots:
signals:
// emitted when an attribute is modified
void modified();
void itemModified(qtItem*);
protected:
void updateAttributeData() override;
......
......@@ -139,6 +139,7 @@ void qtOperationView::createWidget()
this->Internals->m_instancedView.reset(iview);
QObject::connect(iview, SIGNAL(modified()), this, SLOT(onModifiedParameters()));
QObject::connect(iview, SIGNAL(itemModified(qtItem*)), this, SLOT(onModifiedParameter(qtItem*)));
this->Internals->m_applyButton = new QPushButton("Apply", this->Widget);
this->Internals->m_applyButton->setObjectName("OpViewApplyButton");
......@@ -171,6 +172,21 @@ void qtOperationView::onModifiedParameters()
}
}
void qtOperationView::onModifiedParameter(qtItem* uiItem)
{
auto op = this->operation();
if (op && uiItem)
{
// Signal the operation that an item's value has been changed in the UI.
// The attribute pointer is null; that is reserved for when the UI creates
// or destroys an attribute instance.
if (op->configure(nullptr, uiItem->item()))
{
this->attributeChanged(op->parameters());
}
}
}
void qtOperationView::showAdvanceLevelOverlay(bool show)
{
this->Internals->m_instancedView->showAdvanceLevelOverlay(show);
......
......@@ -69,6 +69,7 @@ public slots:
void requestModelEntityAssociation() override;
void onShowCategory() override { this->updateAttributeData(); }
virtual void onModifiedParameters();
virtual void onModifiedParameter(qtItem* item);
virtual void onOperate();
signals:
......
......@@ -88,6 +88,12 @@ Operation::Specification Operation::specification()
return m_specification;
}
bool Operation::configure(const smtk::attribute::AttributePtr&, const smtk::attribute::ItemPtr&)
{
// Do nothing. Subclasses might want to do something, though.
return false;
}
bool Operation::ableToOperate()
{
return this->parameters()->isValid();
......
......@@ -84,6 +84,32 @@ public:
// resolve to the same index).
virtual Index index() const { return std::type_index(typeid(*this)).hash_code(); }
/// Update the operation's specification and operations to be consistent.
///
/// This does nothing by default but subclasses may override this method to
/// update default values (say, based on the current set of associations).
/// This method should be called by user interfaces when the associations
/// (and potentially other attributes/items in the specification) are modified.
///
/// By default, the attribute and item passed are null.
/// If values are passed, the attribute or item **must** belong to the
/// resource for the operation itself, and should indicate changes made
/// by the user.
/// When an attribute is created or removed at the user's behest it is passed.
/// When an item's value(s) are directly edited by a user, then
/// a pointer to it is passed.
/// Only one value (the item or the attribute) should be non-null
/// for a given invocation of configure().
/// Both may be null (for instance, when an operation is being asked to
/// initialize its parameters based on the global application state rather
/// than a particular user input).
///
/// This method should return true if any changes were made to the operation's
/// specification and false otherwise.
virtual bool configure(
const smtk::attribute::AttributePtr& changedAttribute = smtk::attribute::AttributePtr(),
const smtk::attribute::ItemPtr& changedItem = smtk::attribute::ItemPtr());
// Check if the operation's attribute resource is valid. Derived operations
// may implement more task-specific checks to ensure that the operation is in
// a valid state.
......
......@@ -66,6 +66,9 @@ bool Resource::resetDomainTessellation(smtk::model::Volume& domain)
{
tess->addLine(edgeEndpoints[ee][0], edgeEndpoints[ee][1]);
}
double bbox[6] = { origin[0], origin[0] + size[0], origin[1], origin[1] + size[1], origin[2],
origin[2] + size[2] };
domain.setBoundingBox(bbox);
return true;
}
}
......
......@@ -21,17 +21,15 @@
#include "smtk/attribute/IntItem.h"
#include "smtk/attribute/ResourceItem.h"
#include "smtk/attribute/StringItem.h"
#include "smtk/attribute/operators/Signal.h"
#include "smtk/common/UUID.h"
#include "smtk/operation/Manager.h"
#include "smtk/model/AuxiliaryGeometry.h"
#include "smtk/model/Model.h"
#include "smtk/model/Tessellation.h"
#include "smtk/mesh/core/CellTraits.h"
#include "smtk/mesh/core/CellTypes.h"
#include "smtk/mesh/utility/Create.h"
#include "smtk/common/UUID.h"
#include <ctime> // for std::time_t
......@@ -42,6 +40,67 @@ namespace session
namespace oscillator
{
bool EditSource::configure(
const smtk::attribute::AttributePtr&, const smtk::attribute::ItemPtr& changedItem)
{
auto params = this->parameters();
auto assocs = params->associations();
if (!changedItem || changedItem != assocs || assocs->numberOfValues() != 1)
{
return false;
}
auto ent = assocs->valueAs<smtk::model::Entity>(0);
if (!ent)
{
return false;
}
// Copy their values to local fields
smtk::model::FloatList center{ 0., 0., 0. };
smtk::model::FloatList radius{ 0. };
auto source = ent->referenceAs<smtk::model::AuxiliaryGeometry>();
if (source.isValid() && source.hasStringProperty("oscillator_type") &&
source.stringProperty("oscillator_type")[0] == "source")
{
center = source.floatProperty("center");
radius = source.floatProperty("radius");
}
else
{
auto model = ent->referenceAs<smtk::model::Model>();
if (!model.isValid())
{
return false;
}
auto bbox = model.boundingBox();
double minDist = -1.0;
for (int ii = 0; ii < 3; ++ii)
{
center[ii] = 0.5 * (bbox[2 * ii + 0] + bbox[2 * ii + 1]);
double delta = bbox[2 * ii + 1] - bbox[2 * ii + 0];
if (delta > 0.0 && (delta > minDist || minDist < 0.0))
{
minDist = delta;
}
}
radius[0] = minDist / 4;
}
smtk::attribute::DoubleItemPtr centerItem =
this->parameters()->findGroup("location")->findAs<smtk::attribute::DoubleItem>("center");
smtk::attribute::DoubleItemPtr radiusItem =
this->parameters()->findGroup("location")->findAs<smtk::attribute::DoubleItem>("radius");
for (int ii = 0; ii < 3; ii++)
{
centerItem->setValue(ii, center[ii]);
}
radiusItem->setValue(0, radius[0]);
return true;
}
EditSource::Result EditSource::operateInternal()
{
// Access the origin, size and discretization parameters
......
......@@ -30,6 +30,16 @@ public:
smtkCreateMacro(EditSource);
smtkSharedFromThisMacro(smtk::operation::Operation);
/**\brief Change item parameters to match associated source (if any).
*
* When \a changedItem is the ReferenceItem that serves as the
* operation's associations and it contains a single "source" (i.e.,
* a 2-d auxiliary geometry representing an oscillator source),
* update the position and radius of the operation to match.
*/
bool configure(const smtk::attribute::AttributePtr& changedAttribute,
const smtk::attribute::ItemPtr& changedItem) override;
protected:
Result operateInternal() override;
virtual const char* xmlDescription() const override;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment