comparison 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
comparison
equal deleted inserted replaced
1346:df8bf351c23f 1347:bfd77672d825
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #pragma once
23
24 #include "SampleApplicationBase.h"
25
26 #include "../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
27 #include "../../Framework/Deprecated/Layers/LineMeasureTracker.h"
28 #include "../../Framework/Deprecated/SmartLoader.h"
29 #include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
30 #include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
31 #include "../../Framework/Messages/IObserver.h"
32
33 #if ORTHANC_ENABLE_WASM==1
34 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
35 #include "../../Platforms/Wasm/Defaults.h"
36 #endif
37
38 #include <Core/Images/Font.h>
39 #include <Core/Logging.h>
40
41 namespace OrthancStone
42 {
43 namespace Samples
44 {
45 class SimpleViewerApplication :
46 public SampleSingleCanvasWithButtonsApplicationBase,
47 public ObserverBase<SimpleViewerApplication>
48 {
49 private:
50 class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
51 {
52 private:
53 SimpleViewerApplication& application_;
54
55 public:
56 ThumbnailInteractor(SimpleViewerApplication& application) :
57 application_(application)
58 {
59 }
60
61 virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
62 const Deprecated::ViewportGeometry& view,
63 MouseButton button,
64 KeyboardModifiers modifiers,
65 int viewportX,
66 int viewportY,
67 double x,
68 double y,
69 Deprecated::IStatusBar* statusBar,
70 const std::vector<Deprecated::Touch>& displayTouches)
71 {
72 if (button == MouseButton_Left)
73 {
74 statusBar->SetMessage("selected thumbnail " + widget.GetName());
75 std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-"));
76 application_.SelectSeriesInMainViewport(seriesId);
77 }
78 return NULL;
79 }
80
81 virtual void MouseOver(CairoContext& context,
82 Deprecated::WorldSceneWidget& widget,
83 const Deprecated::ViewportGeometry& view,
84 double x,
85 double y,
86 Deprecated::IStatusBar* statusBar)
87 {
88 }
89
90 virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
91 MouseWheelDirection direction,
92 KeyboardModifiers modifiers,
93 Deprecated::IStatusBar* statusBar)
94 {
95 }
96
97 virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
98 KeyboardKeys key,
99 char keyChar,
100 KeyboardModifiers modifiers,
101 Deprecated::IStatusBar* statusBar)
102 {
103 }
104 };
105
106 class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
107 {
108 private:
109 SimpleViewerApplication& application_;
110
111 public:
112 MainWidgetInteractor(SimpleViewerApplication& application) :
113 application_(application)
114 {
115 }
116
117 virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
118 const Deprecated::ViewportGeometry& view,
119 MouseButton button,
120 KeyboardModifiers modifiers,
121 int viewportX,
122 int viewportY,
123 double x,
124 double y,
125 Deprecated::IStatusBar* statusBar,
126 const std::vector<Deprecated::Touch>& displayTouches)
127 {
128 if (button == MouseButton_Left)
129 {
130 if (application_.currentTool_ == Tool_LineMeasure)
131 {
132 return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
133 x, y, 255, 0, 0, application_.GetFont());
134 }
135 else if (application_.currentTool_ == Tool_CircleMeasure)
136 {
137 return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
138 x, y, 255, 0, 0, application_.GetFont());
139 }
140 }
141 return NULL;
142 }
143
144 virtual void MouseOver(CairoContext& context,
145 Deprecated::WorldSceneWidget& widget,
146 const Deprecated::ViewportGeometry& view,
147 double x,
148 double y,
149 Deprecated::IStatusBar* statusBar)
150 {
151 if (statusBar != NULL)
152 {
153 Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
154
155 char buf[64];
156 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
157 p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
158 statusBar->SetMessage(buf);
159 }
160 }
161
162 virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
163 MouseWheelDirection direction,
164 KeyboardModifiers modifiers,
165 Deprecated::IStatusBar* statusBar)
166 {
167 }
168
169 virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
170 KeyboardKeys key,
171 char keyChar,
172 KeyboardModifiers modifiers,
173 Deprecated::IStatusBar* statusBar)
174 {
175 switch (keyChar)
176 {
177 case 's':
178 widget.FitContent();
179 break;
180
181 case 'l':
182 application_.currentTool_ = Tool_LineMeasure;
183 break;
184
185 case 'c':
186 application_.currentTool_ = Tool_CircleMeasure;
187 break;
188
189 default:
190 break;
191 }
192 }
193 };
194
195
196 #if ORTHANC_ENABLE_WASM==1
197 class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter
198 {
199 SimpleViewerApplication& viewerApplication_;
200
201 public:
202 SimpleViewerApplicationAdapter(SimpleViewerApplication& application)
203 : WasmPlatformApplicationAdapter(application),
204 viewerApplication_(application)
205 {
206 }
207
208 virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input)
209 {
210 if (input == "select-tool:line-measure")
211 {
212 viewerApplication_.currentTool_ = Tool_LineMeasure;
213 NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure");
214 }
215 else if (input == "select-tool:circle-measure")
216 {
217 viewerApplication_.currentTool_ = Tool_CircleMeasure;
218 NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure");
219 }
220
221 output = "ok";
222 }
223
224 virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage)
225 {
226 UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
227 }
228
229 virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage)
230 {
231 UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
232 }
233
234 };
235 #endif
236 enum Tool {
237 Tool_LineMeasure,
238 Tool_CircleMeasure
239 };
240
241 Tool currentTool_;
242 std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_;
243 std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_;
244 Deprecated::LayoutWidget* mainLayout_;
245 Deprecated::LayoutWidget* thumbnailsLayout_;
246 std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> > thumbnails_;
247
248 std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
249 std::map<std::string, Json::Value> seriesTags_;
250
251 unsigned int currentInstanceIndex_;
252 Deprecated::WidgetViewport* wasmViewport1_;
253 Deprecated::WidgetViewport* wasmViewport2_;
254
255 Deprecated::IStatusBar* statusBar_;
256 std::unique_ptr<Deprecated::SmartLoader> smartLoader_;
257
258 Orthanc::Font font_;
259
260 public:
261 SimpleViewerApplication() :
262 currentTool_(Tool_LineMeasure),
263 mainLayout_(NULL),
264 currentInstanceIndex_(0),
265 wasmViewport1_(NULL),
266 wasmViewport2_(NULL)
267 {
268 font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
269 // DeclareIgnoredMessage(MessageType_Widget_ContentChanged);
270 }
271
272 virtual void DeclareStartupOptions(boost::program_options::options_description& options)
273 {
274 boost::program_options::options_description generic("Sample options");
275 generic.add_options()
276 ("studyId", boost::program_options::value<std::string>(),
277 "Orthanc ID of the study")
278 ;
279
280 options.add(generic);
281 }
282
283 virtual void Initialize(StoneApplicationContext* context,
284 Deprecated::IStatusBar& statusBar,
285 const boost::program_options::variables_map& parameters)
286 {
287 using namespace OrthancStone;
288
289 context_ = context;
290 statusBar_ = &statusBar;
291
292 {// initialize viewports and layout
293 mainLayout_ = new Deprecated::LayoutWidget("main-layout");
294 mainLayout_->SetPadding(10);
295 mainLayout_->SetBackgroundCleared(true);
296 mainLayout_->SetBackgroundColor(0, 0, 0);
297 mainLayout_->SetHorizontal();
298
299 boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout"));
300 thumbnailsLayout_->SetPadding(10);
301 thumbnailsLayout_->SetBackgroundCleared(true);
302 thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
303 thumbnailsLayout_->SetVertical();
304
305 boost::shared_ptr<Deprecated::SliceViewerWidget> widget
306 (new Deprecated::SliceViewerWidget("main-viewport"));
307 SetCentralWidget(widget);
308 //mainWidget_->RegisterObserver(*this);
309
310 // hierarchy
311 mainLayout_->AddWidget(thumbnailsLayout_);
312 mainLayout_->AddWidget(widget);
313
314 // sources
315 smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient()));
316 smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
317
318 mainLayout_->SetTransmitMouseOver(true);
319 mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
320 widget->SetInteractor(*mainWidgetInteractor_);
321 thumbnailInteractor_.reset(new ThumbnailInteractor(*this));
322 }
323
324 statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
325 statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport");
326
327
328 if (parameters.count("studyId") < 1)
329 {
330 LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
331 context->GetOrthancApiClient()->GetJsonAsync(
332 "/studies",
333 new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
334 (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived));
335 }
336 else
337 {
338 SelectStudy(parameters["studyId"].as<std::string>());
339 }
340 }
341
342 void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
343 {
344 const Json::Value& response = message.GetJson();
345
346 if (response.isArray() &&
347 response.size() >= 1)
348 {
349 SelectStudy(response[0].asString());
350 }
351 }
352
353 void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
354 {
355 const Json::Value& response = message.GetJson();
356
357 if (response.isObject() && response["Series"].isArray())
358 {
359 for (size_t i=0; i < response["Series"].size(); i++)
360 {
361 context_->GetOrthancApiClient()->GetJsonAsync(
362 "/series/" + response["Series"][(int)i].asString(),
363 new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
364 (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived));
365 }
366 }
367 }
368
369 void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
370 {
371 const Json::Value& response = message.GetJson();
372
373 if (response.isObject() &&
374 response["Instances"].isArray() &&
375 response["Instances"].size() > 0)
376 {
377 // keep track of all instances IDs
378 const std::string& seriesId = response["ID"].asString();
379 seriesTags_[seriesId] = response;
380 instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>();
381 for (size_t i = 0; i < response["Instances"].size(); i++)
382 {
383 const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString();
384 instancesIdsPerSeriesId_[seriesId].push_back(instanceId);
385 }
386
387 // load the first instance in the thumbnail
388 LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
389
390 // if this is the first thumbnail loaded, load the first instance in the mainWidget
391 Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
392 if (widget.GetLayerCount() == 0)
393 {
394 smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
395 }
396 }
397 }
398
399 void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
400 {
401 LOG(INFO) << "Loading thumbnail for series " << seriesId;
402 boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId));
403 thumbnails_.push_back(thumbnailWidget);
404 thumbnailsLayout_->AddWidget(thumbnailWidget);
405 Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged);
406 smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
407 thumbnailWidget->SetInteractor(*thumbnailInteractor_);
408 }
409
410 void SelectStudy(const std::string& studyId)
411 {
412 LOG(INFO) << "Selecting study: " << studyId;
413 context_->GetOrthancApiClient()->GetJsonAsync(
414 "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
415 (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived));
416 }
417
418 void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
419 {
420 // TODO: The "const_cast" could probably be replaced by "mainWidget"
421 const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
422 }
423
424 void SelectSeriesInMainViewport(const std::string& seriesId)
425 {
426 Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
427 smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
428 }
429
430 const Orthanc::Font& GetFont() const
431 {
432 return font_;
433 }
434
435 virtual void OnPushButton1Clicked() {}
436 virtual void OnPushButton2Clicked() {}
437 virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;}
438 virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;}
439
440 virtual void GetButtonNames(std::string& pushButton1,
441 std::string& pushButton2,
442 std::string& tool1,
443 std::string& tool2)
444 {
445 tool1 = "line";
446 tool2 = "circle";
447 pushButton1 = "action1";
448 pushButton2 = "action2";
449 }
450
451 #if ORTHANC_ENABLE_WASM==1
452 virtual void InitializeWasm()
453 {
454 AttachWidgetToWasmViewport("canvas", thumbnailsLayout_);
455 AttachWidgetToWasmViewport("canvas2", widget);
456 }
457 #endif
458
459 };
460 }
461 }