Mercurial > hg > orthanc-stone
changeset 325:37ab9d83dc9b am-2
reactivate SingleFrameApplication sample + Added SingleFrameEditorApplication (SDL only)
author | am@osimis.io |
---|---|
date | Tue, 16 Oct 2018 11:30:00 +0200 |
parents | 29a79b8c3d39 |
children | 612238b3f3e8 |
files | Applications/Samples/CMakeLists.txt Applications/Samples/SampleList.h Applications/Samples/SingleFrameApplication.h Applications/Samples/SingleFrameEditorApplication.h |
diffstat | 4 files changed, 280 insertions(+), 86 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/Samples/CMakeLists.txt Tue Oct 16 08:59:23 2018 +0200 +++ b/Applications/Samples/CMakeLists.txt Tue Oct 16 11:30:00 2018 +0200 @@ -123,12 +123,13 @@ #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1) #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2) -#BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) +BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3) #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4) #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5) #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6) #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7) BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8) # we keep that one just as a sample before we convert another sample to this pattern +BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9) ##### SimpleViewer sample (Qt and WASM only) #######
--- a/Applications/Samples/SampleList.h Tue Oct 16 08:59:23 2018 +0200 +++ b/Applications/Samples/SampleList.h Tue Oct 16 11:30:00 2018 +0200 @@ -32,6 +32,10 @@ #include "SimpleViewerApplicationSingleFile.h" typedef OrthancStone::Samples::SimpleViewerApplication SampleApplication; +#elif ORTHANC_STONE_SAMPLE == 9 +#include "SingleFrameEditorApplication.h" +typedef OrthancStone::Samples::SingleFrameEditorApplication SampleApplication; + #else #error Please set the ORTHANC_STONE_SAMPLE macro #endif
--- a/Applications/Samples/SingleFrameApplication.h Tue Oct 16 08:59:23 2018 +0200 +++ b/Applications/Samples/SingleFrameApplication.h Tue Oct 16 11:30:00 2018 +0200 @@ -34,7 +34,7 @@ { class SingleFrameApplication : public SampleApplicationBase, - private ILayerSource::IObserver + public IObserver { private: class Interactor : public IWorldSceneInteractor @@ -51,6 +51,7 @@ virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, const ViewportGeometry& view, MouseButton button, + KeyboardModifiers modifiers, double x, double y, IStatusBar* statusBar) @@ -148,7 +149,7 @@ slice_ = index; #if 1 - widget_->SetSlice(source_->GetSlice(slice_).GetGeometry()); + mainWidget_->SetSlice(source_->GetSlice(slice_).GetGeometry()); #else // TEST for scene extents - Rotate the axes double a = 15.0 / 180.0 * M_PI; @@ -169,46 +170,29 @@ } - virtual void NotifyGeometryReady(const ILayerSource& source) + void OnMainWidgetGeometryReady(const ILayerSource::GeometryReadyMessage& message) { // Once the geometry of the series is downloaded from Orthanc, - // display its first slice, and adapt the viewport to fit this + // display its middle slice, and adapt the viewport to fit this // slice - if (source_ == &source) + if (source_ == &message.origin_) { SetSlice(source_->GetSliceCount() / 2); } - widget_->SetDefaultView(); - } - - virtual void NotifyGeometryError(const ILayerSource& source) - { + mainWidget_->SetDefaultView(); } - virtual void NotifyContentChange(const ILayerSource& source) - { - } + LayerWidget* mainWidget_; // ownership is transfered to the application context + std::unique_ptr<Interactor> mainWidgetInteractor_; + std::unique_ptr<OrthancApiClient> orthancApiClient_; - virtual void NotifySliceChange(const ILayerSource& source, - const Slice& slice) - { - } - - virtual void NotifyLayerReady(std::auto_ptr<ILayerRenderer>& layer, - const ILayerSource& source, - const CoordinateSystem3D& slice, - bool isError) - { - } + const OrthancFrameLayerSource* source_; + unsigned int slice_; - LayerWidget* widget_; - const OrthancFrameLayerSource* source_; - unsigned int slice_; - public: - SingleFrameApplication() : - widget_(NULL), + SingleFrameApplication(MessageBroker& broker) : + IObserver(broker), source_(NULL), slice_(0) { @@ -229,11 +213,14 @@ options.add(generic); } - virtual void Initialize(IStatusBar& statusBar, + virtual void Initialize(StoneApplicationContext* context, + IStatusBar& statusBar, const boost::program_options::variables_map& parameters) { using namespace OrthancStone; + context_ = context; + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); if (parameters.count("instance") != 1) @@ -245,17 +232,14 @@ std::string instance = parameters["instance"].as<std::string>(); int frame = parameters["frame"].as<unsigned int>(); - std::auto_ptr<LayerWidget> widget(new LayerWidget); + orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService())); + mainWidget_ = new LayerWidget(broker_, "main-widget"); -#if 1 - std::auto_ptr<OrthancFrameLayerSource> layer - (new OrthancFrameLayerSource(context_->GetWebService())); - //layer->SetImageQuality(SliceImageQuality_Jpeg50); + std::auto_ptr<OrthancFrameLayerSource> layer(new OrthancFrameLayerSource(broker_, *orthancApiClient_)); + source_ = layer.get(); layer->LoadFrame(instance, frame); - //layer->LoadSeries("6f1b492a-e181e200-44e51840-ef8db55e-af529ab6"); - layer->Register(*this); - source_ = layer.get(); - widget->AddLayer(layer.release()); + layer->RegisterObserverCallback(new Callable<SingleFrameApplication, ILayerSource::GeometryReadyMessage>(*this, &SingleFrameApplication::OnMainWidgetGeometryReady)); + mainWidget_->AddLayer(layer.release()); RenderStyle s; @@ -264,53 +248,17 @@ s.interpolation_ = ImageInterpolation_Bilinear; } - //s.drawGrid_ = true; - widget->SetLayerStyle(0, s); -#else - // 0178023P** - // Extent of the CT layer: (-35.068 -20.368) => (34.932 49.632) - std::auto_ptr<OrthancFrameLayerSource> ct; - ct.reset(new OrthancFrameLayerSource(context_->GetWebService())); - //ct->LoadInstance("c804a1a2-142545c9-33b32fe2-3df4cec0-a2bea6d6", 0); - //ct->LoadInstance("4bd4304f-47478948-71b24af2-51f4f1bc-275b6c1b", 0); // BAD SLICE - //ct->SetImageQuality(SliceImageQuality_Jpeg50); - ct->LoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d"); - - ct->Register(*this); - widget->AddLayer(ct.release()); - - std::auto_ptr<OrthancFrameLayerSource> pet; - pet.reset(new OrthancFrameLayerSource(context_->GetWebService())); - //pet->LoadInstance("a1c4dc6b-255d27f0-88069875-8daed730-2f5ee5c6", 0); - pet->LoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e"); - pet->Register(*this); - source_ = pet.get(); - widget->AddLayer(pet.release()); + mainWidget_->SetLayerStyle(0, s); + mainWidget_->SetTransmitMouseOver(true); - { - RenderStyle s; - //s.drawGrid_ = true; - s.alpha_ = 1; - widget->SetLayerStyle(0, s); - } + mainWidgetInteractor_.reset(new Interactor(*this)); + mainWidget_->SetInteractor(*mainWidgetInteractor_); + } - { - RenderStyle s; - //s.drawGrid_ = true; - s.SetColor(255, 0, 0); // Draw missing PET layer in red - s.alpha_ = 0.5; - s.applyLut_ = true; - s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET; - s.interpolation_ = ImageInterpolation_Bilinear; - widget->SetLayerStyle(1, s); - } -#endif + virtual void Finalize() {} + virtual IWidget* GetCentralWidget() {return mainWidget_;} + }; - widget_ = widget.get(); - widget_->SetTransmitMouseOver(true); - widget_->SetInteractor(context_->AddInteractor(new Interactor(*this))); - context_->SetCentralWidget(widget.release()); - } - }; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SingleFrameEditorApplication.h Tue Oct 16 11:30:00 2018 +0200 @@ -0,0 +1,241 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 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/Layers/OrthancFrameLayerSource.h" +#include "../../Framework/Widgets/LayerWidget.h" + +#include <Core/Logging.h> + +namespace OrthancStone +{ + namespace Samples + { + class SingleFrameEditorApplication : + public SampleApplicationBase, + public IObserver + { + enum Tools + { + Tools_Crop, + Tools_Windowing, + Tools_Zoom, + Tools_Pan + }; + + enum Actions + { + Actions_Invert, + Actions_RotateLeft, + Actions_RotateRight + }; + + private: + class Interactor : public IWorldSceneInteractor + { + private: + SingleFrameEditorApplication& application_; + + public: + Interactor(SingleFrameEditorApplication& application) : + application_(application) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const ViewportGeometry& view, + MouseButton button, + KeyboardModifiers modifiers, + double x, + double y, + IStatusBar* statusBar) + { + switch (application_.currentTool_) { + case Tools_Crop: + case Tools_Windowing: + case Tools_Zoom: + case Tools_Pan: + // TODO return the right mouse tracker + return NULL; + } + + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + if (statusBar != NULL) + { + Vector p = dynamic_cast<LayerWidget&>(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(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + switch (key) + { + case 's': + widget.SetDefaultView(); + break; + case 'p': + application_.currentTool_ = Tools_Pan; + break; + case 'z': + application_.currentTool_ = Tools_Zoom; + break; + case 'c': + application_.currentTool_ = Tools_Crop; + break; + case 'w': + application_.currentTool_ = Tools_Windowing; + break; + case 'i': + application_.Invert(); + break; + case 'r': + if (modifiers == KeyboardModifiers_None) + application_.Rotate(90); + else + application_.Rotate(-90); + break; + case 'e': + application_.Export(); + break; + default: + break; + } + } + }; + + void OnMainWidgetGeometryReady(const ILayerSource::GeometryReadyMessage& message) + { + mainWidget_->SetDefaultView(); + } + + LayerWidget* mainWidget_; // ownership is transfered to the application context + std::unique_ptr<Interactor> mainWidgetInteractor_; + std::unique_ptr<OrthancApiClient> orthancApiClient_; + Tools currentTool_; + + const OrthancFrameLayerSource* source_; + unsigned int slice_; + + public: + SingleFrameEditorApplication(MessageBroker& broker) : + IObserver(broker), + currentTool_(Tools_Zoom), + source_(NULL), + slice_(0) + { + } + + virtual void DeclareStartupOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("instance", boost::program_options::value<std::string>(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value<unsigned int>()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ; + + options.add(generic); + } + + virtual void Initialize(StoneApplicationContext* context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + context_ = context; + + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout, \"p\" to pan, \"z\" to zoom, \"c\" to crop, \"i\" to invert, \"w\" to change windowing, \"r\" to rotate cw, \"shift+r\" to rotate ccw"); + + if (parameters.count("instance") != 1) + { + LOG(ERROR) << "The instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string instance = parameters["instance"].as<std::string>(); + int frame = parameters["frame"].as<unsigned int>(); + + orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService())); + mainWidget_ = new LayerWidget(broker_, "main-widget"); + + std::auto_ptr<OrthancFrameLayerSource> layer(new OrthancFrameLayerSource(broker_, *orthancApiClient_)); + source_ = layer.get(); + layer->LoadFrame(instance, frame); + layer->RegisterObserverCallback(new Callable<SingleFrameEditorApplication, ILayerSource::GeometryReadyMessage>(*this, &SingleFrameEditorApplication::OnMainWidgetGeometryReady)); + mainWidget_->AddLayer(layer.release()); + + mainWidget_->SetTransmitMouseOver(true); + + mainWidgetInteractor_.reset(new Interactor(*this)); + mainWidget_->SetInteractor(*mainWidgetInteractor_); + } + + virtual void Finalize() {} + virtual IWidget* GetCentralWidget() {return mainWidget_;} + + void Invert() + { + // TODO + } + + void Rotate(int degrees) + { + // TODO + } + + void Export() + { + // TODO: export dicom file to a temporary file + } + }; + + + } +}