Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add actions plugin #29

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ add_subdirectory(ExampleTransformation)
add_subdirectory(ExampleLoader)
add_subdirectory(ExampleWriter)
add_subdirectory(ExampleData)
add_subdirectory(ExampleActions)
139 changes: 139 additions & 0 deletions ExampleActions/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
cmake_minimum_required(VERSION 3.17)

option(MV_UNITY_BUILD "Combine target source files into batches for faster compilation" OFF)

# -----------------------------------------------------------------------------
# ExampleView Plugin
# -----------------------------------------------------------------------------
PROJECT("ExampleActionsPlugin")

# -----------------------------------------------------------------------------
# CMake Options
# -----------------------------------------------------------------------------
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DWIN32 /EHsc /MP /permissive- /Zc:__cplusplus")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:LIBCMT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
endif(MSVC)

# -----------------------------------------------------------------------------
# Set install directory
# -----------------------------------------------------------------------------
# Check if the directory to the ManiVault installation has been provided
if(NOT DEFINED MV_INSTALL_DIR)
set(MV_INSTALL_DIR "" CACHE PATH "Directory where ManiVault is installed")
message(FATAL_ERROR "Please set MV_INSTALL_DIR to the directory where ManiVault is installed")
endif()
file(TO_CMAKE_PATH ${MV_INSTALL_DIR} MV_INSTALL_DIR)

# -----------------------------------------------------------------------------
# Dependencies
# -----------------------------------------------------------------------------
find_package(Qt6 COMPONENTS Widgets WebEngineWidgets REQUIRED)

# -----------------------------------------------------------------------------
# Source files
# -----------------------------------------------------------------------------
# Define the plugin sources
set(PLUGIN_SOURCES
src/ExampleActionsPlugin.h
src/ExampleActionsPlugin.cpp
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(Model FILES ${MODEL})
source_group(Actions FILES ${ACTIONS})

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

# -----------------------------------------------------------------------------
# Target include directories
# -----------------------------------------------------------------------------
# Include ManiVault headers, including system data plugins
target_include_directories(${PROJECT_NAME} PRIVATE "${MV_INSTALL_DIR}/$<CONFIGURATION>/include/")

# -----------------------------------------------------------------------------
# Target properties
# -----------------------------------------------------------------------------
# Request C++17
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)

# Enable unity build
if(MV_UNITY_BUILD)
set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON)
endif()

# -----------------------------------------------------------------------------
# Target library linking
# -----------------------------------------------------------------------------
# Link to Qt libraries
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::WebEngineWidgets)

# Link to ManiVault and data plugins
# The link path in this repo assumes that the ManiVault core was built locally
# in contrast to having been installed with an installer. In the latter case you'll have
# to adapt the MV_LINK_PATH and PLUGIN_LINK_PATH to your install folder
set(MV_LINK_PATH "${MV_INSTALL_DIR}/$<CONFIGURATION>/lib")
set(PLUGIN_LINK_PATH "${MV_INSTALL_DIR}/$<CONFIGURATION>/$<IF:$<CXX_COMPILER_ID:MSVC>,lib,Plugins>")
set(MV_LINK_SUFFIX $<IF:$<CXX_COMPILER_ID:MSVC>,${CMAKE_LINK_LIBRARY_SUFFIX},${CMAKE_SHARED_LIBRARY_SUFFIX}>)

set(MV_LINK_LIBRARY "${MV_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}MV_Public${MV_LINK_SUFFIX}")
set(POINTDATA_LINK_LIBRARY "${PLUGIN_LINK_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}PointData${MV_LINK_SUFFIX}")

target_link_libraries(${PROJECT_NAME} PRIVATE "${MV_LINK_LIBRARY}")
target_link_libraries(${PROJECT_NAME} PRIVATE "${POINTDATA_LINK_LIBRARY}")

# -----------------------------------------------------------------------------
# Target installation
# -----------------------------------------------------------------------------
# Install the shared plugin libary to the "Plugins" folder in the ManiVault install directory
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION Plugins COMPONENT PLUGINS # Windows .dll
LIBRARY DESTINATION Plugins COMPONENT PLUGINS # Linux/Mac .so
)

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}"
--install ${CMAKE_CURRENT_BINARY_DIR}
--config $<CONFIGURATION>
--prefix ${MV_INSTALL_DIR}/$<CONFIGURATION>
)

# -----------------------------------------------------------------------------
# Miscellaneous
# -----------------------------------------------------------------------------
# Automatically set the debug environment (command + working directory) for MSVC
if(MSVC)
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $<IF:$<CONFIG:DEBUG>,${MV_INSTALL_DIR}/debug,${MV_INSTALL_DIR}/release>)
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_COMMAND $<IF:$<CONFIG:DEBUG>,${MV_INSTALL_DIR}/debug/ManiVault\ Studio.exe,${MV_INSTALL_DIR}/release/ManiVault\ Studio.exe>)
endif()
10 changes: 10 additions & 0 deletions ExampleActions/src/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
root = true

# All files
[*]
indent_style = space

# Cpp files
[*.{h,cpp}]
indent_size = 4
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);
};
104 changes: 104 additions & 0 deletions ExampleActions/src/ExampleActionsPlugin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#include "ExampleActionsPlugin.h"
#include "ExampleProxyAction.h"

#include <CoreInterface.h>

#include <QDebug>
#include <QSplitter>
#include <QHeaderView>

Q_PLUGIN_METADATA(IID "studio.manivault.ExampleActionsPlugin")

using namespace mv;

ExampleActionsPlugin::ExampleActionsPlugin(const PluginFactory* 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& treeView = hierarchyWidget->getTreeView();

//treeView.setRootIsDecorated(false);
treeView.setTextElideMode(Qt::ElideMiddle);

treeView.setColumnHidden(static_cast<int>(ExampleActionsTreeModel::Column::ClassName), true);

auto treeViewHeader = treeView.header();

treeViewHeader->setSectionResizeMode(static_cast<int>(ExampleActionsTreeModel::Column::Name), QHeaderView::Stretch);
treeViewHeader->setSectionResizeMode(static_cast<int>(ExampleActionsTreeModel::Column::Description), QHeaderView::Stretch);

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(verticalGroupAction, "ExampleProxy", action));

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

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

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

_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()));

splitter->setStretchFactor(0, 1);
splitter->setStretchFactor(1, 3);

layout->addWidget(splitter);

getWidget().setLayout(layout);
}

ViewPlugin* ExampleActionsPluginFactory::produce()
{
return new ExampleActionsPlugin(this);
}
Loading
Loading