Skip to content

Commit

Permalink
First working version
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasKroes committed Jun 19, 2024
1 parent 5eb468c commit 0a06d7d
Show file tree
Hide file tree
Showing 11 changed files with 474 additions and 5 deletions.
21 changes: 19 additions & 2 deletions ExampleActions/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,34 @@ set(PLUGIN_SOURCES
src/ExampleActionsPlugin.json
)

set(MODEL
src/AbstractExampleActionsModel.h
src/AbstractExampleActionsModel.cpp
src/ExampleActionsTreeModel.h
src/ExampleActionsTreeModel.cpp
src/ExampleActionsFilterModel.h
src/ExampleActionsFilterModel.cpp
)

set(ACTIONS
src/ExampleProxyAction.h
src/ExampleProxyAction.cpp
)

set(PLUGIN_MOC_HEADERS
src/ExampleActionsPlugin.h
src/ExampleActionsTreeModel.h
)

source_group( Plugin FILES ${PLUGIN_SOURCES})
source_group(Plugin FILES ${PLUGIN_SOURCES})
source_group(Model FILES ${MODEL})
source_group(Actions FILES ${ACTIONS})

# -----------------------------------------------------------------------------
# CMake Target
# -----------------------------------------------------------------------------
# Create dynamic library for the plugin
add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES})
add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES} ${MODEL} ${ACTIONS})

# -----------------------------------------------------------------------------
# Target include directories
Expand Down
36 changes: 36 additions & 0 deletions ExampleActions/src/AbstractExampleActionsModel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// A corresponding LICENSE file is located in the root directory of this source tree
// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft)

#include "AbstractExampleActionsModel.h"

#include <actions/WidgetAction.h>

#include <util/Exception.h>

using namespace mv;
using namespace mv::util;
using namespace mv::gui;

#ifdef _DEBUG
#define ABSTRACT_EXAMPLE_ACTIONS_MODEL_VERBOSE
#endif

AbstractExampleActionsModel::ExampleActionItem::ExampleActionItem(const QString& title, mv::gui::WidgetAction* action) :
QStandardItem(title),
QObject(),
_action(action)
{
setEditable(false);
setDropEnabled(false);

Q_ASSERT(_action != nullptr);

if (!_action)
return;
}

mv::gui::WidgetAction* AbstractExampleActionsModel::ExampleActionItem::getAction() const
{
return _action;
}
52 changes: 52 additions & 0 deletions ExampleActions/src/AbstractExampleActionsModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// A corresponding LICENSE file is located in the root directory of this source tree
// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft)

#pragma once

#include <models/StandardItemModel.h>

#include <QStandardItem>

namespace mv::gui {
class WidgetAction;
}

/**
* Abstract example actions model class
*
* @author Thomas Kroes
*/
class AbstractExampleActionsModel : public mv::StandardItemModel
{
protected:

/** Base standard model item class for example widget action */
class ExampleActionItem : public QStandardItem, public QObject {
public:

/**
* Construct with pointer to \p action
* @param title Item title
* @param action Pointer to action to display item for
*/
ExampleActionItem(const QString& title, mv::gui::WidgetAction* action);

/**
* Get action
* return Pointer to action to display item for
*/
mv::gui::WidgetAction* getAction() const;

private:
mv::gui::WidgetAction* _action; /** Pointer to action to display item for */
};

public:

/** No need for custom constructor */
using mv::StandardItemModel::StandardItemModel;

friend class ExampleActionStyledItemDelegate;
friend class ExampleActionsPlugin;
};
17 changes: 17 additions & 0 deletions ExampleActions/src/ExampleActionsFilterModel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// A corresponding LICENSE file is located in the root directory of this source tree
// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft)

#include "ExampleActionsFilterModel.h"

#include <QDebug>

#ifdef _DEBUG
#define EXAMPLE_ACTIONS_FILTER_MODEL_VERBOSE
#endif

ExampleActionsFilterModel::ExampleActionsFilterModel(QObject* parent /*= nullptr*/) :
SortFilterProxyModel(parent)
{
setRecursiveFilteringEnabled(true);
}
25 changes: 25 additions & 0 deletions ExampleActions/src/ExampleActionsFilterModel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// A corresponding LICENSE file is located in the root directory of this source tree
// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft)

#pragma once

#include <models/SortFilterProxyModel.h>

/**
* Example actions filter model class
*
* Sorting and filtering model for example actions
*
* @author Thomas Kroes
*/
class ExampleActionsFilterModel : public mv::SortFilterProxyModel
{
public:

/**
* Construct the filter model with \p parent
* @param parent Pointer to parent object
*/
ExampleActionsFilterModel(QObject* parent = nullptr);
};
64 changes: 61 additions & 3 deletions ExampleActions/src/ExampleActionsPlugin.cpp
Original file line number Diff line number Diff line change
@@ -1,26 +1,84 @@
#include "ExampleActionsPlugin.h"
#include "ExampleProxyAction.h"

#include <CoreInterface.h>

#include <QDebug>
#include <QSplitter>

Q_PLUGIN_METADATA(IID "studio.manivault.ExampleActionsPlugin")

using namespace mv;

ExampleActionsPlugin::ExampleActionsPlugin(const PluginFactory* factory) :
ViewPlugin(factory)
ViewPlugin(factory),
_exampleActionsTreeModel(this),
_exampleActionsFilterModel(this),
_examplesTreeAction(this, "Examples tree"),
_examplesGroupsAction(this, "Examples groups")
{
_examplesTreeAction.setIconByName("play");

_examplesTreeAction.setWidgetConfigurationFunction([this](WidgetAction* action, QWidget* widget) -> void {
auto hierarchyWidget = widget->findChild<HierarchyWidget*>("HierarchyWidget");

Q_ASSERT(hierarchyWidget);

if (!hierarchyWidget)
return;

auto selectionModel = hierarchyWidget->getTreeView().selectionModel();

connect(selectionModel, &QItemSelectionModel::selectionChanged, hierarchyWidget, [this, selectionModel](const QItemSelection& selected, const QItemSelection& deselected) -> void {
_examplesGroupsAction.resetGroupActions();

QSet<QModelIndex> selectedModelIndexes;

for (const auto& selectedIndex : selectionModel->selectedRows(static_cast<int>(ExampleActionsTreeModel::Column::ClassName))) {
const auto nameIndex = selectedIndex.siblingAtColumn(static_cast<int>(ExampleActionsTreeModel::Column::Name));

if (nameIndex.parent().isValid()) {
selectedModelIndexes << selectedIndex;
}
else {
for (int rowIndex = 0; rowIndex < _exampleActionsFilterModel.rowCount(nameIndex); rowIndex++)
selectedModelIndexes << _exampleActionsFilterModel.index(rowIndex, static_cast<int>(ExampleActionsTreeModel::Column::ClassName), nameIndex);
}
}

for (const auto& selectedModelIndex : selectedModelIndexes) {
auto actionItem = _exampleActionsTreeModel.itemFromIndex(_exampleActionsFilterModel.mapToSource(selectedModelIndex));
auto action = dynamic_cast<AbstractExampleActionsModel::ExampleActionItem*>(actionItem)->getAction();
auto name = QString("%1/%2").arg(selectedModelIndex.parent().data().toString(), selectedModelIndex.data().toString());
auto verticalGroupAction = new VerticalGroupAction(this, name, true);

verticalGroupAction->setDefaultWidgetFlag(VerticalGroupAction::WidgetFlag::NoMargins);
verticalGroupAction->setShowLabels(false);
verticalGroupAction->addAction(new ExampleProxyAction(this, "", action));

_examplesGroupsAction.addGroupAction(verticalGroupAction);
}
});
});
}

void ExampleActionsPlugin::init()
{
// Create layout
auto layout = new QVBoxLayout();

layout->setContentsMargins(0, 0, 0, 0);

// Apply the layout
_examplesTreeAction.initialize(&_exampleActionsTreeModel, &_exampleActionsFilterModel, "Example action");

auto splitter = new QSplitter();

splitter->setOrientation(Qt::Orientation::Horizontal);

splitter->addWidget(_examplesTreeAction.createWidget(&getWidget()));
splitter->addWidget(_examplesGroupsAction.createWidget(&getWidget()));

layout->addWidget(splitter);

getWidget().setLayout(layout);
}

Expand Down
15 changes: 15 additions & 0 deletions ExampleActions/src/ExampleActionsPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

#include <ViewPlugin.h>

#include <actions/TreeAction.h>
#include <actions/GroupsAction.h>

#include "ExampleActionsTreeModel.h"
#include "ExampleActionsFilterModel.h"

#include <QWidget>
#include <QListView>

/** All plugin related classes are in the ManiVault plugin namespace */
using namespace mv::plugin;
Expand Down Expand Up @@ -43,6 +50,14 @@ class ExampleActionsPlugin : public ViewPlugin

/** This function is called by the core after the view plugin has been created */
void init() override;

protected:
ExampleActionsTreeModel _exampleActionsTreeModel; /** Standard item model which stores example actions grouped by category */
ExampleActionsFilterModel _exampleActionsFilterModel; /** Sort/filter proxy model for ExampleActionsPlugin#_exampleActionsTreeModel */
mv::gui::TreeAction _examplesTreeAction; /** For displaying the ExampleActionsPlugin#_exampleActionsFilterModel */
mv::gui::GroupsAction _examplesGroupsAction; /** For displaying the ExampleActionsPlugin#_exampleActionsListModel */

friend class ExampleActionStyledItemDelegate;
};

/**
Expand Down
107 changes: 107 additions & 0 deletions ExampleActions/src/ExampleActionsTreeModel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// A corresponding LICENSE file is located in the root directory of this source tree
// Copyright (C) 2023 BioVault (Biomedical Visual Analytics Unit LUMC - TU Delft)

#include "ExampleActionsTreeModel.h"

#include <actions/WidgetAction.h>

#include <util/Exception.h>

using namespace mv;
using namespace mv::util;
using namespace mv::gui;

#ifdef _DEBUG
#define EXAMPLE_ACTIONS_TREE_MODEL_VERBOSE
#endif

ExampleActionsTreeModel::ExampleActionsTreeModel(QObject* parent /*= nullptr*/) :
AbstractExampleActionsModel(parent)
{
setColumnCount(static_cast<int>(Column::Count));

appendRow(CategoryRow("Color", {
{ "Color action", "For picking colors", "mv::gui::ColorAction" },
{ "1D Color map action", "For configuring one-dimensional color maps", "mv::gui::ColorMap1DAction" },
{ "2D Color map action", "For configuring two-dimensional color maps", "mv::gui::ColorMap2DAction" }
}));

appendRow(CategoryRow("File", {
{ "File picker", "For picking file location", "mv::gui::FilePickerAction" },
{ "Directory picker", "For picking directories", "mv::gui::DirectoryPickerAction" }
}));

appendRow(CategoryRow("Grouping", {
{ "Horizontal group", "For laying out actions horizontally", "mv::gui::HorizontalGroupAction" },
{ "Vertical group", "For laying out actions vertically", "mv::gui::VerticalGroupAction" },
{ "Groups", "Vertical groups accordion action", "mv::gui::GroupsAction" },
{ "Horizontal toolbar", "Horizontal group action that changes the state of action based on the available width ", "mv::gui::HorizontalToolbarAction" },
{ "Stretch", "Adds stretch to a group action", "mv::gui::StretchAction" }
}));

appendRow(CategoryRow("Miscellaneous", {
{ "Dataset picker", "For picking datasets", "mv::gui::DatasetPickerAction" }
}));
}

QVariant ExampleActionsTreeModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
{
switch (static_cast<Column>(section))
{
case Column::Name:
return "Name";

case Column::Description:
return "Description";

case Column::ClassName:
return "Class name";

default:
break;
}

return {};
}

ExampleActionsTreeModel::CategoryRow::CategoryRow(const QString& category, const QList<QStringList>& actions) : QList<QStandardItem*>()
{
auto categoryItem = new QStandardItem(category);

for (const auto& action : actions) {
QList<QStandardItem*> items;

try {
for (const auto& string : action) {

const auto metaType = action[2];
const auto metaTypeId = QMetaType::type(metaType.toLatin1());
const auto metaObject = QMetaType::metaObjectForType(metaTypeId);

if (!metaObject)
throw std::runtime_error(QString("Meta object type '%1' is not known. Did you forget to register the action correctly with Qt meta object system? See ToggleAction.h for an example.").arg(metaType).toLatin1());

auto metaObjectInstance = metaObject->newInstance(Q_ARG(QObject*, nullptr), Q_ARG(QString, QString("Example%1").arg(metaType)));
auto exampleAction = dynamic_cast<WidgetAction*>(metaObjectInstance);

if (!exampleAction)
throw std::runtime_error(QString("Unable to create a new instance of type '%1'").arg(metaType).toLatin1());

items << new ExampleActionItem(string, exampleAction);
}
}
catch (std::exception& e)
{
exceptionMessageBox("Unable to load example action:", e);
}
catch (...)
{
exceptionMessageBox("Unable to load example action:");
}

categoryItem->appendRow(items);
}

append(categoryItem);
}
Loading

0 comments on commit 0a06d7d

Please sign in to comment.