Mercurial > hg > orthanc-stone
diff Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h @ 1347:bfd77672d825 broker
Moved Application/Samples/* to Application/Samples/Deprecated/*
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Tue, 07 Apr 2020 14:29:01 +0200 |
parents | Applications/Samples/SimpleViewerApplicationSingleFile.h@257f2c9a02ac |
children | c53a4667f895 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Tue Apr 07 14:29:01 2020 +0200 @@ -0,0 +1,461 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../../Framework/Deprecated/Layers/CircleMeasureTracker.h" +#include "../../Framework/Deprecated/Layers/LineMeasureTracker.h" +#include "../../Framework/Deprecated/SmartLoader.h" +#include "../../Framework/Deprecated/Widgets/LayoutWidget.h" +#include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h" +#include "../../Framework/Messages/IObserver.h" + +#if ORTHANC_ENABLE_WASM==1 +#include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" +#include "../../Platforms/Wasm/Defaults.h" +#endif + +#include <Core/Images/Font.h> +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class SimpleViewerApplication : + public SampleSingleCanvasWithButtonsApplicationBase, + public ObserverBase<SimpleViewerApplication> + { + private: + class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + + public: + ThumbnailInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + statusBar->SetMessage("selected thumbnail " + widget.GetName()); + std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); + application_.SelectSeriesInMainViewport(seriesId); + } + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + }; + + class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor + { + private: + SimpleViewerApplication& application_; + + public: + MainWidgetInteractor(SimpleViewerApplication& application) : + application_(application) + { + } + + virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + int viewportX, + int viewportY, + double x, + double y, + Deprecated::IStatusBar* statusBar, + const std::vector<Deprecated::Touch>& displayTouches) + { + if (button == MouseButton_Left) + { + if (application_.currentTool_ == Tool_LineMeasure) + { + return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + else if (application_.currentTool_ == Tool_CircleMeasure) + { + return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(), + x, y, 255, 0, 0, application_.GetFont()); + } + } + return NULL; + } + + virtual void MouseOver(CairoContext& context, + Deprecated::WorldSceneWidget& widget, + const Deprecated::ViewportGeometry& view, + double x, + double y, + Deprecated::IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", + p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + virtual void MouseWheel(Deprecated::WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + } + + virtual void KeyPressed(Deprecated::WorldSceneWidget& widget, + KeyboardKeys key, + char keyChar, + KeyboardModifiers modifiers, + Deprecated::IStatusBar* statusBar) + { + switch (keyChar) + { + case 's': + widget.FitContent(); + break; + + case 'l': + application_.currentTool_ = Tool_LineMeasure; + break; + + case 'c': + application_.currentTool_ = Tool_CircleMeasure; + break; + + default: + break; + } + } + }; + + +#if ORTHANC_ENABLE_WASM==1 + class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter + { + SimpleViewerApplication& viewerApplication_; + + public: + SimpleViewerApplicationAdapter(SimpleViewerApplication& application) + : WasmPlatformApplicationAdapter(application), + viewerApplication_(application) + { + } + + virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) + { + if (input == "select-tool:line-measure") + { + viewerApplication_.currentTool_ = Tool_LineMeasure; + NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure"); + } + else if (input == "select-tool:circle-measure") + { + viewerApplication_.currentTool_ = Tool_CircleMeasure; + NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure"); + } + + output = "ok"; + } + + virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) + { + UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str()); + } + + virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) + { + UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str()); + } + + }; +#endif + enum Tool { + Tool_LineMeasure, + Tool_CircleMeasure + }; + + Tool currentTool_; + std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; + std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; + Deprecated::LayoutWidget* mainLayout_; + Deprecated::LayoutWidget* thumbnailsLayout_; + std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> > thumbnails_; + + std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_; + std::map<std::string, Json::Value> seriesTags_; + + unsigned int currentInstanceIndex_; + Deprecated::WidgetViewport* wasmViewport1_; + Deprecated::WidgetViewport* wasmViewport2_; + + Deprecated::IStatusBar* statusBar_; + std::unique_ptr<Deprecated::SmartLoader> smartLoader_; + + Orthanc::Font font_; + + public: + SimpleViewerApplication() : + currentTool_(Tool_LineMeasure), + mainLayout_(NULL), + currentInstanceIndex_(0), + wasmViewport1_(NULL), + wasmViewport2_(NULL) + { + font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); +// DeclareIgnoredMessage(MessageType_Widget_ContentChanged); + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("studyId", boost::program_options::value<std::string>(), + "Orthanc ID of the study") + ; + + options.add(generic); + } + + virtual void Initialize(StoneApplicationContext* context, + Deprecated::IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + context_ = context; + statusBar_ = &statusBar; + + {// initialize viewports and layout + mainLayout_ = new Deprecated::LayoutWidget("main-layout"); + mainLayout_->SetPadding(10); + mainLayout_->SetBackgroundCleared(true); + mainLayout_->SetBackgroundColor(0, 0, 0); + mainLayout_->SetHorizontal(); + + boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout")); + thumbnailsLayout_->SetPadding(10); + thumbnailsLayout_->SetBackgroundCleared(true); + thumbnailsLayout_->SetBackgroundColor(50, 50, 50); + thumbnailsLayout_->SetVertical(); + + boost::shared_ptr<Deprecated::SliceViewerWidget> widget + (new Deprecated::SliceViewerWidget("main-viewport")); + SetCentralWidget(widget); + //mainWidget_->RegisterObserver(*this); + + // hierarchy + mainLayout_->AddWidget(thumbnailsLayout_); + mainLayout_->AddWidget(widget); + + // sources + smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient())); + smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam); + + mainLayout_->SetTransmitMouseOver(true); + mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); + widget->SetInteractor(*mainWidgetInteractor_); + thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); + } + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); + + + if (parameters.count("studyId") < 1) + { + LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; + context->GetOrthancApiClient()->GetJsonAsync( + "/studies", + new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived)); + } + else + { + SelectStudy(parameters["studyId"].as<std::string>()); + } + } + + void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isArray() && + response.size() >= 1) + { + SelectStudy(response[0].asString()); + } + } + + void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && response["Series"].isArray()) + { + for (size_t i=0; i < response["Series"].size(); i++) + { + context_->GetOrthancApiClient()->GetJsonAsync( + "/series/" + response["Series"][(int)i].asString(), + new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived)); + } + } + } + + void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + const Json::Value& response = message.GetJson(); + + if (response.isObject() && + response["Instances"].isArray() && + response["Instances"].size() > 0) + { + // keep track of all instances IDs + const std::string& seriesId = response["ID"].asString(); + seriesTags_[seriesId] = response; + instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); + for (size_t i = 0; i < response["Instances"].size(); i++) + { + const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); + instancesIdsPerSeriesId_[seriesId].push_back(instanceId); + } + + // load the first instance in the thumbnail + LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); + + // if this is the first thumbnail loaded, load the first instance in the mainWidget + Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); + if (widget.GetLayerCount() == 0) + { + smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + } + } + + void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) + { + LOG(INFO) << "Loading thumbnail for series " << seriesId; + boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId)); + thumbnails_.push_back(thumbnailWidget); + thumbnailsLayout_->AddWidget(thumbnailWidget); + Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged); + smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); + thumbnailWidget->SetInteractor(*thumbnailInteractor_); + } + + void SelectStudy(const std::string& studyId) + { + LOG(INFO) << "Selecting study: " << studyId; + context_->GetOrthancApiClient()->GetJsonAsync( + "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage> + (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived)); + } + + void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message) + { + // TODO: The "const_cast" could probably be replaced by "mainWidget" + const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent(); + } + + void SelectSeriesInMainViewport(const std::string& seriesId) + { + Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget()); + smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0); + } + + const Orthanc::Font& GetFont() const + { + return font_; + } + + virtual void OnPushButton1Clicked() {} + virtual void OnPushButton2Clicked() {} + virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;} + virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;} + + virtual void GetButtonNames(std::string& pushButton1, + std::string& pushButton2, + std::string& tool1, + std::string& tool2) + { + tool1 = "line"; + tool2 = "circle"; + pushButton1 = "action1"; + pushButton2 = "action2"; + } + +#if ORTHANC_ENABLE_WASM==1 + virtual void InitializeWasm() + { + AttachWidgetToWasmViewport("canvas", thumbnailsLayout_); + AttachWidgetToWasmViewport("canvas2", widget); + } +#endif + + }; + } +}