Commit bcd98a1d authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

Improve layout support in Python.

To fix #18964, pqMultiViewWidget no longer
automatically grabs a newly created view. Instead association of a view
with a layout is now an explicit action. This makes it possible to
faithfully reproduce Python traces when played back in the Qt GUI.

Since all views are now created 'detached', the APIs to create detached
views is being removed since it's no longer applicable.

Python state support was also imporved to correctly record view layouts.
Previously, layouts were simply not saved in Python state files.

SplitViewTrace and TraceMultiViews tests now do baseline compares to
validate the fixes.

Fixes #18964.
parent 87e8ad6f
8564fdd6a0a285674ad23fa3a81e2e1112a353b2175ff412dd18a9e2fb525907ff096a63e29b90f0f46debc9980c372c1df4081457cb5fa16e16c49cf4d8cdea
274156243e67892bb05e2c4546ea028c74a0c96e7ed737df914f6dce5c13e37c651e386b0b1300ef4439d44c4a28c176123ab08297d547bc662dba7c3670df5b
07a84d8e55df9a071987eda1900cc2ec590f9b2fa3a6acd072cb814d92a9c32ea79f884d99f9ebfa05ddfd3a31bd42697b7fcf398ba0fab7402fd0256abc3159
......@@ -307,6 +307,7 @@ ExternalData_Expand_Arguments(ParaViewData _
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/SliceWithPlaneMultiBlockSurface_1.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/SpherePointSource_InterA.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/SpherePointSource_InterB.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/SplitViewTraceScreenshot.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/StepColorSpaceA.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/StructuredGridCellBlanking.png}"
"DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data/Baseline/StructuredGridVolumeRendering.png}"
......@@ -426,7 +427,6 @@ endif ()
if (PARAVIEW_ENABLE_PYTHON)
list(APPEND TESTS_WITHOUT_BASELINES
DetachedFromLayoutIndirect.xml
TestPythonConsole.xml
TestPopOutWidget.xml # needs programmable filter
MultipleNumberOfComponents.xml
......@@ -978,6 +978,7 @@ if(PARAVIEW_ENABLE_PYTHON)
RestoreArrayDefaultTransferFunction.xml
RestoreDefaultTransferFunction.xml
TraceMultiViews.xml
SplitViewTrace.xml
)
list(APPEND TESTS_WITHOUT_BASELINES
PythonCalculator.xml
......
<?xml version="1.0" ?>
<pqevents>
<pqevent object="pqClientMainWindow/centralwidget/MultiViewWidget/CoreWidget/qt_tabwidget_tabbar" command="set_tab_with_text" arguments="+" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menu_View" />
<pqevent object="pqClientMainWindow/menubar/menu_View" command="activate" arguments="Python Shell" />
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/consoleWidget" command="executeCommand" arguments="CreateView(&quot;RenderView&quot;, True)" />
<pqevent object="pqClientMainWindow/centralwidget/MultiViewWidget/CoreWidget/qt_tabwidget_stackedwidget/MultiViewWidget2/Container/Frame.0/CentralWidgetFrame/EmptyView/scrollArea/qt_scrollarea_viewport/widgetFoo/ConvertActionsFrame/SpreadSheetView" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/consoleWidget" command="executeCommand" arguments="CreateView(&quot;RenderView&quot;, False)" />
<pqevent object="pqClientMainWindow/menubar/menu_View" command="activate" arguments="Python Shell" />
</pqevents>
This diff is collapsed.
......@@ -12,8 +12,6 @@
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/variableToolbar/actionScalarBarVisibility" command="set_boolean" arguments="false" />
<pqevent object="pqClientMainWindow/cameraToolbar/actionNegativeY" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuTools" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionTesting_Window_Size" />
<pqevent object="pqClientMainWindow/cameraToolbar/actionResetCamera" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/VCRToolbar/actionVCRNextFrame" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/VCRToolbar/actionVCRNextFrame" command="activate" arguments="" />
......@@ -38,16 +36,6 @@
<pqevent object="pqClientMainWindow/centralwidget/MultiViewWidget/CoreWidget/qt_tabwidget_stackedwidget/MultiViewWidget1/Container/Frame.1" command="mouseRelease" arguments="1,0,0,221,22" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menu_File" />
<!-- save a multiview screenshot -->
<pqevent object="pqClientMainWindow/menubar/menu_File" command="activate" arguments="actionFileSaveScreenshot" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsScreenshot.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsScreenshot.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/SaveAllViews/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/width" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/height" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/widget/OKButton" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuTools" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionToolsStartStopTrace" />
......@@ -63,6 +51,17 @@
<pqevent object="pqClientMainWindow/FileSaveServerStateDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsState.py" />
<pqevent object="StateOptionsDialog/widget/OKButton" command="activate" arguments="" />
<!-- save a multiview screenshot -->
<pqevent object="pqClientMainWindow/menubar/menu_File" command="activate" arguments="actionFileSaveScreenshot" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/SaveAllViews/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/width" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/height" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/widget/OKButton" command="activate" arguments="" />
<!-- compare screenshot saved during xml test playback -->
<pqcompareimage image="$PARAVIEW_TEST_ROOT/TraceMultiViews.png" baseline="$PARAVIEW_DATA_ROOT/Applications/ParaView/Testing/Data/Baseline/TraceMultiViews.png" />
<!-- now reset the session and play back the trace -->
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menu_Edit" />
......@@ -70,16 +69,20 @@
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menu_View" />
<pqevent object="pqClientMainWindow/menubar/menu_View" command="activate" arguments="Python Shell" />
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/runScriptButton" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/PythonShellRunScriptDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsScreenshot.png" />
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/PythonShellRunScriptDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsTrace.py" />
<pqevent object="pqClientMainWindow/pythonShellDock/qt_dockwidget_closebutton" command="activate" arguments="" />
<!-- compare the image saved in the trace -->
<!-- I'm skipping the actual compare right now. We need to fix how charts
render with mag>1, and also the fact that the spreadsheet header is too wide. -->
<!--
<pqcompareview image="$PARAVIEW_TEST_ROOT/TraceMultiViewsScreenshot.png" baseline="$PARAVIEW_DATA_ROOT/Applications/ParaView/Testing/Data/Baseline/TraceMultiViewsScreenshot.png" />
-->
<!-- save a multiview screenshot -->
<pqevent object="pqClientMainWindow/menubar/menu_File" command="activate" arguments="actionFileSaveScreenshot" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews-Trace.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews-Trace.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/SaveAllViews/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/width" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/height" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/widget/OKButton" command="activate" arguments="" />
<!-- compare screenshot saved during trace playback -->
<pqcompareimage image="$PARAVIEW_TEST_ROOT/TraceMultiViews-Trace.png" baseline="$PARAVIEW_DATA_ROOT/Applications/ParaView/Testing/Data/Baseline/TraceMultiViews.png" />
<!-- now reset session and load the state file -->
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menu_Edit" />
......@@ -94,8 +97,18 @@
<pqevent object="pqClientMainWindow/pythonShellDock/pythonShell/PythonShellRunScriptDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViewsState.py" />
<pqevent object="pqClientMainWindow/pythonShellDock/qt_dockwidget_closebutton" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionTesting_Window_Size" />
<!-- save a multiview screenshot -->
<pqevent object="pqClientMainWindow/menubar/menu_File" command="activate" arguments="actionFileSaveScreenshot" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews-State.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotFileDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/TraceMultiViews-State.png" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/SaveAllViews/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/width" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/scrollArea/qt_scrollarea_viewport/Container/ProxyWidget/ImageResolution/height" command="set_string" arguments="800" />
<pqevent object="pqClientMainWindow/SaveScreenshotDialog/widget/OKButton" command="activate" arguments="" />
<!-- FIXME: this test doesn't do baseline compares yet. Some issue with state
not saving view layout and GUI not respecting active view from Python need to be fixed first. -->
<!-- compare screenshot saved during trace playback -->
<!--
We skip this compare currently since state doesn't save current time
<pqcompareimage image="$PARAVIEW_TEST_ROOT/TraceMultiViews-State.png" baseline="$PARAVIEW_DATA_ROOT/Applications/ParaView/Testing/Data/Baseline/TraceMultiViews.png" />
-->
</pqevents>
# Layouts and Python support
Mutliple views and layouts are now better traced in Python. When the generated
trace is played back in the GUI, the views are laid out as expected. Also Python
state can now capture the view layout state faithfully.
Developers and Python users must note that view creation APIs no longer
automatically assign views to a layout. One must use explicit function calls to
assign a view to a layout after creation.
......@@ -713,53 +713,6 @@ void vtkSMParaViewPipelineControllerWithRendering::UpdatePipelineBeforeDisplay(
producer->UpdatePipeline(time);
}
//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineControllerWithRendering::RegisterViewProxy(
vtkSMProxy* proxy, const char* proxyname)
{
if (!proxy)
{
return false;
}
bool retval = this->Superclass::RegisterViewProxy(proxy, proxyname);
if (proxy->HasAnnotation("ParaView::DetachedFromLayout") &&
strcmp(proxy->GetAnnotation("ParaView::DetachedFromLayout"), "true") == 0)
{
return retval;
}
vtkSMSessionProxyManager* pxm = proxy->GetSessionProxyManager();
// locate layout (create a new one if needed).
vtkSMProxySelectionModel* selmodel = pxm->GetSelectionModel("ActiveView");
assert(selmodel != nullptr);
vtkSMViewProxy* activeView = vtkSMViewProxy::SafeDownCast(selmodel->GetCurrentProxy());
vtkSMProxy* activeLayout = vtkSMViewLayoutProxy::FindLayout(activeView);
activeLayout =
activeLayout ? activeLayout : this->FindProxy(pxm, "layouts", "misc", "ViewLayout");
if (!activeLayout)
{
// no active layout is present at all. Create a new one.
activeLayout = pxm->NewProxy("misc", "ViewLayout");
if (activeLayout)
{
this->InitializeProxy(activeLayout);
this->RegisterLayoutProxy(activeLayout);
activeLayout->FastDelete();
}
}
if (activeLayout)
{
vtkSMProxy* layoutAssigned =
vtkSMViewLayoutProxy::FindLayout(vtkSMViewProxy::SafeDownCast(proxy));
activeLayout = layoutAssigned ? layoutAssigned : activeLayout;
vtkSMViewLayoutProxy::SafeDownCast(activeLayout)
->AssignViewToAnyCell(vtkSMViewProxy::SafeDownCast(proxy), 0);
}
return retval;
}
//----------------------------------------------------------------------------
bool vtkSMParaViewPipelineControllerWithRendering::RegisterLayoutProxy(
vtkSMProxy* proxy, const char* proxyname)
......@@ -789,3 +742,56 @@ void vtkSMParaViewPipelineControllerWithRendering::DoMaterialSetup(vtkSMProxy* p
mlp->LoadDefaultMaterials();
mlp->Synchronize();
}
//----------------------------------------------------------------------------
void vtkSMParaViewPipelineControllerWithRendering::AssignViewToLayout(
vtkSMViewProxy* view, vtkSMViewLayoutProxy* layout, int hint)
{
if (!view)
{
vtkErrorMacro("`AssignViewToLayout` called with `view==nullptr`.");
return;
}
// sanity check, the view cannot be assigned to another layout already.
if (vtkSMViewLayoutProxy::FindLayout(view) != nullptr)
{
return;
}
if (layout && layout->GetSession() != view->GetSession())
{
// both layout and view must be on the same session.
layout = nullptr;
}
SM_SCOPED_TRACE(CallFunction)
.arg("AssignViewToLayout")
.arg("view", view)
.arg("layout", layout)
.arg("hint", hint)
.arg("comment", "add view to a layout so it's visible in UI");
auto pxm = view->GetSessionProxyManager();
if (layout == nullptr)
{
layout =
vtkSMViewLayoutProxy::SafeDownCast(this->FindProxy(pxm, "layouts", "misc", "ViewLayout"));
}
if (layout == nullptr)
{
// create a new layout.
if ((layout = vtkSMViewLayoutProxy::SafeDownCast(pxm->NewProxy("misc", "ViewLayout"))))
{
this->InitializeProxy(layout);
this->RegisterLayoutProxy(layout);
layout->FastDelete();
}
}
if (layout)
{
layout->AssignViewToAnyCell(view, hint);
}
}
......@@ -197,18 +197,23 @@ public:
*/
bool PostInitializeProxy(vtkSMProxy* proxy) override;
//@{
/**
* Overridden to place the view in a layout on creation.
* Register layout proxy.
*/
bool RegisterViewProxy(vtkSMProxy* proxy, const char* proxyname) override;
using Superclass::RegisterViewProxy;
//@}
virtual bool RegisterLayoutProxy(vtkSMProxy* proxy, const char* proxyname = NULL);
/**
* Register layout proxy.
* Assigns the view to any cell in the layout. If the layout is null, then
* this will locate a layout on the same session and use it. If no layout is
* present on the session, a new layout will be created (and registered)
* before assigning the view to it.
*
* If the view is already assigned to a layout then this method is a no-op.
*
* @sa vtkSMViewProxy::AssignViewToAnyCell
*/
virtual bool RegisterLayoutProxy(vtkSMProxy* proxy, const char* proxyname = NULL);
virtual void AssignViewToLayout(
vtkSMViewProxy* view, vtkSMViewLayoutProxy* layout = nullptr, int hint = 0);
protected:
vtkSMParaViewPipelineControllerWithRendering();
......
......@@ -597,12 +597,12 @@ bool vtkSMViewLayoutProxy::AssignView(int location, vtkSMViewProxy* view)
return false;
}
SM_SCOPED_TRACE(CallMethod)
.arg(this)
.arg("AssignView")
.arg(location)
.arg(view)
.arg("assign view to a particular cell in the layout");
SM_SCOPED_TRACE(CallFunction)
.arg("AssignViewToLayout")
.arg("view", view)
.arg("layout", this)
.arg("hint", location)
.arg("comment", "assign view to a particular cell in the layout");
if (cell.ViewProxy == view)
{
......
......@@ -32,7 +32,9 @@
#include "vtkRendererCollection.h"
#include "vtkSMParaViewPipelineControllerWithRendering.h"
#include "vtkSMProperty.h"
#include "vtkSMProxyIterator.h"
#include "vtkSMProxyManager.h"
#include "vtkSMProxyProperty.h"
#include "vtkSMRepresentationProxy.h"
#include "vtkSMSession.h"
#include "vtkSMSessionProxyManager.h"
......@@ -773,3 +775,31 @@ bool vtkSMViewProxy::MakeRenderWindowInteractor(bool quiet)
this->SetupInteractor(iren);
return this->GetInteractor() != NULL;
}
//----------------------------------------------------------------------------
vtkSMViewProxy* vtkSMViewProxy::FindView(vtkSMProxy* repr, const char* reggroup /*=views*/)
{
if (!repr)
{
return nullptr;
}
vtkSMSessionProxyManager* pxm = repr->GetSessionProxyManager();
vtkNew<vtkSMProxyIterator> iter;
iter->SetSessionProxyManager(pxm);
iter->SetModeToOneGroup();
for (iter->Begin(reggroup); !iter->IsAtEnd(); iter->Next())
{
if (vtkSMViewProxy* view = vtkSMViewProxy::SafeDownCast(iter->GetProxy()))
{
auto reprs = vtkSMProxyProperty::SafeDownCast(view->GetProperty("Representations"));
auto hreprs = vtkSMProxyProperty::SafeDownCast(view->GetProperty("HiddenRepresentations"));
if ((reprs != nullptr && reprs->IsProxyAdded(repr)) ||
(hreprs != nullptr && hreprs->IsProxyAdded(repr)))
{
return view;
}
}
}
return nullptr;
}
......@@ -240,6 +240,12 @@ public:
}
}
//@}
/**
* Helper method to locate a view to which the representation has been added.
*/
static vtkSMViewProxy* FindView(vtkSMProxy* repr, const char* reggroup = "views");
protected:
vtkSMViewProxy();
~vtkSMViewProxy() override;
......
......@@ -280,6 +280,10 @@ void pqApplyBehavior::showData(pqPipelineSource* source, pqView* view)
vtkSMViewProxy* currentViewProxy = view ? view->getViewProxy() : NULL;
const auto& activeObjects = pqActiveObjects::instance();
auto activeLayout = activeObjects.activeLayout();
const auto location = activeObjects.activeLayoutLocation();
QSet<vtkSMProxy*> updated_views;
// create representations for all output ports.
......@@ -291,9 +295,13 @@ void pqApplyBehavior::showData(pqPipelineSource* source, pqView* view)
{
continue;
}
// if new view was created, let's make sure it is assigned to a layout.
controller->AssignViewToLayout(preferredView, activeLayout, location);
updated_views.insert(preferredView);
// if active layout changed, let's use that from this point on.
activeLayout = activeObjects.activeLayout();
// reset camera if this is the only visible dataset.
pqView* pqPreferredView = smmodel->findItem<pqView*>(preferredView);
assert(pqPreferredView);
......
......@@ -152,22 +152,16 @@ void pqDefaultViewBehavior::onServerCreation(pqServer* server)
if (core->getServerManagerModel()->getNumberOfItems<pqView*>() == 0 && server->isMaster())
{
pqObjectBuilder* builder = pqApplicationCore::instance()->getObjectBuilder();
// before creating a view, ensure that a layout (vtkSMViewLayoutProxy) is
// present.
if (server->proxyManager()->GetNumberOfProxies("layouts") == 0)
{
vtkSMProxy* vlayout = builder->createProxy("misc", "ViewLayout", server, "layouts");
assert(vlayout != NULL);
(void)vlayout;
}
QString curView = vtkPVGeneralSettings::GetInstance()->GetDefaultViewType();
if (curView != "None" && !curView.isEmpty() &&
const QString viewType = vtkPVGeneralSettings::GetInstance()->GetDefaultViewType();
if (viewType != "None" && !viewType.isEmpty() &&
RCInfo::Supports(this->ClientCapabilities, RCInfo::OPENGL))
{
// When a server is created, we create a new render view for it.
builder->createView(curView, server);
if (auto pqview = builder->createView(viewType, server))
{
// let's put this view under a layout.
builder->addToLayout(pqview);
}
}
}
......
......@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "pqDataQueryReaction.h"
#include "pqEditCameraReaction.h"
#include "pqInterfaceTracker.h"
#include "pqMultiViewWidget.h"
#include "pqObjectBuilder.h"
#include "pqRenameProxyReaction.h"
#include "pqRenderView.h"
......@@ -52,7 +53,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "pqToggleInteractionViewMode.h"
#include "pqUndoStack.h"
#include "pqViewFrame.h"
#include "vtkChart.h"
#include "vtkCollection.h"
#include "vtkPVProxyDefinitionIterator.h"
......@@ -63,6 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "vtkSMRenderViewProxy.h"
#include "vtkSMSessionProxyManager.h"
#include "vtkSMTooltipSelectionPipeline.h"
#include "vtkSMViewLayoutProxy.h"
#include "vtkSmartPointer.h"
#include <QGuiApplication>
......@@ -75,6 +76,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
namespace
{
template <typename T>
T findParent(QObject* obj)
{
if (auto view = qobject_cast<T>(obj))
{
return view;
}
else if (obj)
{
return findParent<T>(obj->parent());
}
return nullptr;
}
}
//-----------------------------------------------------------------------------
pqStandardViewFrameActionsImplementation::pqStandardViewFrameActionsImplementation(
QObject* parentObject)
......@@ -596,16 +614,18 @@ void pqStandardViewFrameActionsImplementation::aboutToShowConvertMenu()
if (menu)
{
menu->clear();
auto viewframe = ::findParent<pqViewFrame*>(menu);
assert(viewframe != nullptr);
QList<ViewType> views = this->availableViewTypes();
foreach (const ViewType& type, views)
{
QAction* view_action = new QAction(type.Label, menu);
view_action->setProperty("PV_VIEW_TYPE", type.Name);
view_action->setProperty("PV_VIEW_LABEL", type.Label);
view_action->setProperty("PV_COMMAND", "Convert To");
menu->addAction(view_action);
QObject::connect(
view_action, SIGNAL(triggered()), this, SLOT(invoked()), Qt::QueuedConnection);
QObject::connect(view_action, &QAction::triggered, this,
[viewframe, type, this](bool) { this->invoked(viewframe, type, "Convert To"); },
Qt::QueuedConnection);
}
}
}
......@@ -616,46 +636,51 @@ void pqStandardViewFrameActionsImplementation::setupEmptyFrame(QWidget* frame)
Ui::EmptyView ui;
ui.setupUi(frame);
auto viewframe = ::findParent<pqViewFrame*>(frame);
assert(viewframe != nullptr);
QList<ViewType> views = this->availableViewTypes();
foreach (const ViewType& type, views)
{
QPushButton* button = new QPushButton(type.Label, ui.ConvertActionsFrame);
button->setObjectName(type.Name);
button->setProperty("PV_VIEW_TYPE", type.Name);
button->setProperty("PV_VIEW_LABEL", type.Label);
button->setProperty("PV_COMMAND", "Create");
QObject::connect(button, SIGNAL(clicked()), this, SLOT(invoked()), Qt::QueuedConnection);
QObject::connect(button, &QPushButton::clicked, this,
[viewframe, type, this]() { this->invoked(viewframe, type, "Create"); },
Qt::QueuedConnection);
ui.ConvertActionsFrame->layout()->addWidget(button);
}
}
//-----------------------------------------------------------------------------
void pqStandardViewFrameActionsImplementation::invoked()
void pqStandardViewFrameActionsImplementation::invoked(pqViewFrame* viewframe,
const pqStandardViewFrameActionsImplementation::ViewType& vtype, const QString& command)
{
QObject* osender = this->sender();
if (!osender)
if (!viewframe)
{
return;
}
// this implementation is a little hackish.
// pqStandardViewFrameActionsImplementation is ripe for refactoring.
auto pqmvwidget = ::findParent<pqMultiViewWidget*>(viewframe);
assert(pqmvwidget != nullptr);
int frameIndex = viewframe->property("FRAME_INDEX").toInt();
viewframe = nullptr;
// either create a new view, or convert the existing one.
// This slot is called either from an action in the "Convert To" menu, or from
// the buttons on an empty frame.
QString type = osender->property("PV_VIEW_TYPE").toString();
QString label = osender->property("PV_VIEW_LABEL").toString();
QString command = osender->property("PV_COMMAND").toString();
BEGIN_UNDO_SET(QString("%1 %2").arg(command).arg(label));
ViewType vtype;
vtype.Label = label;
vtype.Name = type;
this->handleCreateView(vtype);
BEGIN_UNDO_SET(QString("%1 %2").arg(command).arg(vtype.Label));
if (auto view = this->handleCreateView(vtype))
{
// note: handleCreateView may destroy the pqViewFrame.
// assign it to layout.
pqmvwidget->layoutManager()->AssignViewToAnyCell(view->getViewProxy(), frameIndex);
}
END_UNDO_SET();
}
//-----------------------------------------------------------------------------
void pqStandardViewFrameActionsImplementation::handleCreateView(
pqView* pqStandardViewFrameActionsImplementation::handleCreateView(
const pqStandardViewFrameActionsImplementation::ViewType& viewType)
{
pqObjectBuilder* builder = pqApplicationCore::instance()->getObjectBuilder();
......@@ -667,8 +692,9 @@ void pqStandardViewFrameActionsImplementation::handleCreateView(
}
if (viewType.Name != "None")
{
builder->createView(viewType.Name, pqActiveObjects::instance().activeServer());
return builder->createView(viewType.Name, pqActiveObjects::instance().activeServer());
}
return nullptr;
}
//-----------------------------------------------------------------------------
......
......@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
class pqContextView;
class pqRenderView;
class pqSpreadSheetView;
class pqView;
class QAction;
class QActionGroup;
class QShortcut;
......@@ -110,12 +111,6 @@ protected slots:
*/
void aboutToShowConvertMenu();
/**
* This slot is called either from an action in the "Convert To" menu, or from
* the buttons on an empty frame.
*/
void invoked();
/**
* slots for various shortcuts.
*/
......@@ -212,7 +207,13 @@ protected:
* Called when user clicks "Convert To" or create a view from the empty
* frame.
*/
virtual void handleCreateView(const ViewType& viewType);
virtual pqView* handleCreateView(const ViewType& viewType);
/**
* This is called either from an action in the "Convert To" menu, or from
* the buttons on an empty frame.
*/
void invoked(pqViewFrame*, const ViewType& type, const QString& command);
private:
Q_DISABLE_COPY(pqStandardViewFrameActionsImplementation)
......
......@@ -32,7 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "pqActiveObjects.h"
#include "pqApplicationCore.h"
#include "pqMultiViewWidget.h"
#include "pqServerManagerModel.h"
#include "pqTabbedMultiViewWidget.h"
#include "vtkEventQtSlotConnect.h"
#include "vtkSMOutputPort.h"
#include "vtkSMProxyManager.h"
......@@ -40,7 +42,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "vtkSMSession.h"
#include "vtkSMViewLayoutProxy.h"
#include <QDebug>
#include <algorithm>
//-----------------------------------------------------------------------------
pqActiveObjects& pqActiveObjects::instance()
......@@ -477,7 +479,7 @@ void pqActiveObjects::setSelection(
}
else
{
qCritical() << "Selections with heterogeneous servers are not supported.";
qCritical("Selections with heterogeneous servers are not supported.");
return;
}
}
......@@ -533,7 +535,43 @@ vtkSMViewLayoutProxy* pqActiveObjects::activeLayout() const
{
if (pqView* view = this->activeView())
{
return vtkSMViewLayoutProxy::FindLayout(view->getViewProxy());
if (auto l = vtkSMViewLayoutProxy::FindLayout(view->getViewProxy()))
{
return l;
}
}
auto server = this->activeServer();
if (!server)
{
// if not active server, then don't even attempt to deduce the active layout
// using the pqTabbedMultiViewWidget since that doesn't matter, there's no
// active server.
return nullptr;
}
// if no active view is present, let's attempt to look for
// pqTabbedMultiViewWidget and use its current tab if it is on the active
// server.
auto core = pqApplicationCore::instance();
auto tmvwidget = qobject_cast<pqTabbedMultiViewWidget*>(core->manager("MULTIVIEW_WIDGET"));
auto layoutProxy = tmvwidget ? tmvwidget->layoutProxy() : nullptr;
return (layoutProxy && server->session() == layoutProxy->GetSession()) ? layoutProxy : nullptr;
}
//-----------------------------------------------------------------------------
int pqActiveObjects::activeLayoutLocation() const
{
if (auto layout = this->activeLayout())
{
auto core = pqApplicationCore::instance();
if (auto tmvwidget = qobject_cast<pqTabbedMultiViewWidget*>(core->manager("MULTIVIEW_WIDGET")))
{
if (auto mvwidget = tmvwidget->findTab(layout))
{
return std::max(mvwidget->activeFrameLocation(), 0);
}
}
}
return nullptr;
return 0;
}