Mercurial > hg > orthanc-stone
comparison Applications/Samples/SimpleViewerApplicationSingleFile.h @ 319:daa04d15192c am-2
new SimpleViewer sample that has been split in multiple files to be able to scale it
author | am@osimis.io |
---|---|
date | Thu, 11 Oct 2018 13:16:54 +0200 |
parents | |
children | 612238b3f3e8 |
comparison
equal
deleted
inserted
replaced
318:3a4ca166fafa | 319:daa04d15192c |
---|---|
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-2018 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/Layers/OrthancFrameLayerSource.h" | |
27 #include "../../Framework/Layers/CircleMeasureTracker.h" | |
28 #include "../../Framework/Layers/LineMeasureTracker.h" | |
29 #include "../../Framework/Widgets/LayerWidget.h" | |
30 #include "../../Framework/Widgets/LayoutWidget.h" | |
31 #include "../../Framework/Messages/IObserver.h" | |
32 #include "../../Framework/SmartLoader.h" | |
33 | |
34 #if ORTHANC_ENABLE_WASM==1 | |
35 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h" | |
36 #include "../../Platforms/Wasm/Defaults.h" | |
37 #endif | |
38 | |
39 #if ORTHANC_ENABLE_QT==1 | |
40 #include "Qt/SampleMainWindow.h" | |
41 #endif | |
42 #include <Core/Logging.h> | |
43 | |
44 namespace OrthancStone | |
45 { | |
46 namespace Samples | |
47 { | |
48 class SimpleViewerApplication : | |
49 public SampleApplicationBase, | |
50 public IObserver | |
51 { | |
52 private: | |
53 class ThumbnailInteractor : public IWorldSceneInteractor | |
54 { | |
55 private: | |
56 SimpleViewerApplication& application_; | |
57 public: | |
58 ThumbnailInteractor(SimpleViewerApplication& application) : | |
59 application_(application) | |
60 { | |
61 } | |
62 | |
63 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | |
64 const ViewportGeometry& view, | |
65 MouseButton button, | |
66 KeyboardModifiers modifiers, | |
67 double x, | |
68 double y, | |
69 IStatusBar* statusBar) | |
70 { | |
71 if (button == MouseButton_Left) | |
72 { | |
73 statusBar->SetMessage("selected thumbnail " + widget.GetName()); | |
74 std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-")); | |
75 application_.SelectSeriesInMainViewport(seriesId); | |
76 } | |
77 return NULL; | |
78 } | |
79 virtual void MouseOver(CairoContext& context, | |
80 WorldSceneWidget& widget, | |
81 const ViewportGeometry& view, | |
82 double x, | |
83 double y, | |
84 IStatusBar* statusBar) | |
85 {} | |
86 | |
87 virtual void MouseWheel(WorldSceneWidget& widget, | |
88 MouseWheelDirection direction, | |
89 KeyboardModifiers modifiers, | |
90 IStatusBar* statusBar) | |
91 {} | |
92 | |
93 virtual void KeyPressed(WorldSceneWidget& widget, | |
94 char key, | |
95 KeyboardModifiers modifiers, | |
96 IStatusBar* statusBar) | |
97 {} | |
98 | |
99 }; | |
100 | |
101 class MainWidgetInteractor : public IWorldSceneInteractor | |
102 { | |
103 private: | |
104 SimpleViewerApplication& application_; | |
105 | |
106 public: | |
107 MainWidgetInteractor(SimpleViewerApplication& application) : | |
108 application_(application) | |
109 { | |
110 } | |
111 | |
112 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | |
113 const ViewportGeometry& view, | |
114 MouseButton button, | |
115 KeyboardModifiers modifiers, | |
116 double x, | |
117 double y, | |
118 IStatusBar* statusBar) | |
119 { | |
120 if (button == MouseButton_Left) | |
121 { | |
122 if (application_.currentTool_ == Tools_LineMeasure) | |
123 { | |
124 return new LineMeasureTracker(statusBar, dynamic_cast<LayerWidget&>(widget).GetSlice(), x, y, 255, 0, 0, 10); | |
125 } | |
126 else if (application_.currentTool_ == Tools_CircleMeasure) | |
127 { | |
128 return new CircleMeasureTracker(statusBar, dynamic_cast<LayerWidget&>(widget).GetSlice(), x, y, 255, 0, 0, 10); | |
129 } | |
130 } | |
131 return NULL; | |
132 } | |
133 | |
134 virtual void MouseOver(CairoContext& context, | |
135 WorldSceneWidget& widget, | |
136 const ViewportGeometry& view, | |
137 double x, | |
138 double y, | |
139 IStatusBar* statusBar) | |
140 { | |
141 if (statusBar != NULL) | |
142 { | |
143 Vector p = dynamic_cast<LayerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); | |
144 | |
145 char buf[64]; | |
146 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", | |
147 p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); | |
148 statusBar->SetMessage(buf); | |
149 } | |
150 } | |
151 | |
152 virtual void MouseWheel(WorldSceneWidget& widget, | |
153 MouseWheelDirection direction, | |
154 KeyboardModifiers modifiers, | |
155 IStatusBar* statusBar) | |
156 { | |
157 } | |
158 | |
159 virtual void KeyPressed(WorldSceneWidget& widget, | |
160 char key, | |
161 KeyboardModifiers modifiers, | |
162 IStatusBar* statusBar) | |
163 { | |
164 switch (key) | |
165 { | |
166 case 's': | |
167 widget.SetDefaultView(); | |
168 break; | |
169 | |
170 default: | |
171 break; | |
172 } | |
173 } | |
174 }; | |
175 | |
176 | |
177 #if ORTHANC_ENABLE_WASM==1 | |
178 class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter | |
179 { | |
180 SimpleViewerApplication& viewerApplication_; | |
181 | |
182 public: | |
183 SimpleViewerApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application) | |
184 : WasmPlatformApplicationAdapter(broker, application), | |
185 viewerApplication_(application) | |
186 { | |
187 | |
188 } | |
189 | |
190 virtual void HandleMessageFromWeb(std::string& output, const std::string& input) { | |
191 if (input == "select-tool:line-measure") | |
192 { | |
193 viewerApplication_.currentTool_ = Tools_LineMeasure; | |
194 NotifyStatusUpdateFromCppToWeb("currentTool=line-measure"); | |
195 } | |
196 else if (input == "select-tool:circle-measure") | |
197 { | |
198 viewerApplication_.currentTool_ = Tools_CircleMeasure; | |
199 NotifyStatusUpdateFromCppToWeb("currentTool=circle-measure"); | |
200 } | |
201 | |
202 output = "ok"; | |
203 } | |
204 | |
205 virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) { | |
206 UpdateStoneApplicationStatusFromCpp(statusUpdateMessage.c_str()); | |
207 } | |
208 | |
209 }; | |
210 #endif | |
211 enum Tools { | |
212 Tools_LineMeasure, | |
213 Tools_CircleMeasure | |
214 }; | |
215 | |
216 Tools currentTool_; | |
217 std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_; | |
218 std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; | |
219 LayoutWidget* mainLayout_; | |
220 LayoutWidget* thumbnailsLayout_; | |
221 LayerWidget* mainWidget_; | |
222 std::vector<LayerWidget*> thumbnails_; | |
223 std::map<std::string, std::vector<std::string>> instancesIdsPerSeriesId_; | |
224 std::map<std::string, Json::Value> seriesTags_; | |
225 | |
226 unsigned int currentInstanceIndex_; | |
227 OrthancStone::WidgetViewport* wasmViewport1_; | |
228 OrthancStone::WidgetViewport* wasmViewport2_; | |
229 | |
230 IStatusBar* statusBar_; | |
231 std::unique_ptr<SmartLoader> smartLoader_; | |
232 std::unique_ptr<OrthancApiClient> orthancApiClient_; | |
233 | |
234 public: | |
235 SimpleViewerApplication(MessageBroker& broker) : | |
236 IObserver(broker), | |
237 currentTool_(Tools_LineMeasure), | |
238 mainLayout_(NULL), | |
239 currentInstanceIndex_(0), | |
240 wasmViewport1_(NULL), | |
241 wasmViewport2_(NULL) | |
242 { | |
243 // DeclareIgnoredMessage(MessageType_Widget_ContentChanged); | |
244 } | |
245 | |
246 virtual void Finalize() {} | |
247 virtual IWidget* GetCentralWidget() {return mainLayout_;} | |
248 | |
249 virtual void DeclareStartupOptions(boost::program_options::options_description& options) | |
250 { | |
251 boost::program_options::options_description generic("Sample options"); | |
252 generic.add_options() | |
253 ("studyId", boost::program_options::value<std::string>(), | |
254 "Orthanc ID of the study") | |
255 ; | |
256 | |
257 options.add(generic); | |
258 } | |
259 | |
260 virtual void Initialize(StoneApplicationContext* context, | |
261 IStatusBar& statusBar, | |
262 const boost::program_options::variables_map& parameters) | |
263 { | |
264 using namespace OrthancStone; | |
265 | |
266 context_ = context; | |
267 statusBar_ = &statusBar; | |
268 | |
269 {// initialize viewports and layout | |
270 mainLayout_ = new LayoutWidget("main-layout"); | |
271 mainLayout_->SetPadding(10); | |
272 mainLayout_->SetBackgroundCleared(true); | |
273 mainLayout_->SetBackgroundColor(0, 0, 0); | |
274 mainLayout_->SetHorizontal(); | |
275 | |
276 thumbnailsLayout_ = new LayoutWidget("thumbnail-layout"); | |
277 thumbnailsLayout_->SetPadding(10); | |
278 thumbnailsLayout_->SetBackgroundCleared(true); | |
279 thumbnailsLayout_->SetBackgroundColor(50, 50, 50); | |
280 thumbnailsLayout_->SetVertical(); | |
281 | |
282 mainWidget_ = new LayerWidget(broker_, "main-viewport"); | |
283 //mainWidget_->RegisterObserver(*this); | |
284 | |
285 // hierarchy | |
286 mainLayout_->AddWidget(thumbnailsLayout_); | |
287 mainLayout_->AddWidget(mainWidget_); | |
288 | |
289 orthancApiClient_.reset(new OrthancApiClient(IObserver::broker_, context_->GetWebService())); | |
290 | |
291 // sources | |
292 smartLoader_.reset(new SmartLoader(IObserver::broker_, *orthancApiClient_)); | |
293 smartLoader_->SetImageQuality(SliceImageQuality_FullPam); | |
294 | |
295 mainLayout_->SetTransmitMouseOver(true); | |
296 mainWidgetInteractor_.reset(new MainWidgetInteractor(*this)); | |
297 mainWidget_->SetInteractor(*mainWidgetInteractor_); | |
298 thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); | |
299 } | |
300 | |
301 statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); | |
302 statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); | |
303 | |
304 | |
305 if (parameters.count("studyId") < 1) | |
306 { | |
307 LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; | |
308 orthancApiClient_->GetJsonAsync("/studies", new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived)); | |
309 } | |
310 else | |
311 { | |
312 SelectStudy(parameters["studyId"].as<std::string>()); | |
313 } | |
314 } | |
315 | |
316 void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message) | |
317 { | |
318 const Json::Value& response = message.Response; | |
319 | |
320 if (response.isArray() && response.size() > 1) | |
321 { | |
322 SelectStudy(response[0].asString()); | |
323 } | |
324 } | |
325 void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message) | |
326 { | |
327 const Json::Value& response = message.Response; | |
328 | |
329 if (response.isObject() && response["Series"].isArray()) | |
330 { | |
331 for (size_t i=0; i < response["Series"].size(); i++) | |
332 { | |
333 orthancApiClient_->GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived)); | |
334 } | |
335 } | |
336 } | |
337 | |
338 void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message) | |
339 { | |
340 const Json::Value& response = message.Response; | |
341 | |
342 if (response.isObject() && response["Instances"].isArray() && response["Instances"].size() > 0) | |
343 { | |
344 // keep track of all instances IDs | |
345 const std::string& seriesId = response["ID"].asString(); | |
346 seriesTags_[seriesId] = response; | |
347 instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>(); | |
348 for (size_t i = 0; i < response["Instances"].size(); i++) | |
349 { | |
350 const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString(); | |
351 instancesIdsPerSeriesId_[seriesId].push_back(instanceId); | |
352 } | |
353 | |
354 // load the first instance in the thumbnail | |
355 LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]); | |
356 | |
357 // if this is the first thumbnail loaded, load the first instance in the mainWidget | |
358 if (mainWidget_->GetLayerCount() == 0) | |
359 { | |
360 smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); | |
361 } | |
362 } | |
363 } | |
364 | |
365 void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) | |
366 { | |
367 LOG(INFO) << "Loading thumbnail for series " << seriesId; | |
368 LayerWidget* thumbnailWidget = new LayerWidget(IObserver::broker_, "thumbnail-series-" + seriesId); | |
369 thumbnails_.push_back(thumbnailWidget); | |
370 thumbnailsLayout_->AddWidget(thumbnailWidget); | |
371 thumbnailWidget->RegisterObserverCallback(new Callable<SimpleViewerApplication, LayerWidget::GeometryChangedMessage>(*this, &SimpleViewerApplication::OnWidgetGeometryChanged)); | |
372 smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0); | |
373 thumbnailWidget->SetInteractor(*thumbnailInteractor_); | |
374 } | |
375 | |
376 void SelectStudy(const std::string& studyId) | |
377 { | |
378 orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived)); | |
379 } | |
380 | |
381 void OnWidgetGeometryChanged(const LayerWidget::GeometryChangedMessage& message) | |
382 { | |
383 message.origin_.SetDefaultView(); | |
384 } | |
385 | |
386 void SelectSeriesInMainViewport(const std::string& seriesId) | |
387 { | |
388 smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0); | |
389 } | |
390 | |
391 virtual void OnPushButton1Clicked() {} | |
392 virtual void OnPushButton2Clicked() {} | |
393 virtual void OnTool1Clicked() { currentTool_ = Tools_LineMeasure;} | |
394 virtual void OnTool2Clicked() { currentTool_ = Tools_CircleMeasure;} | |
395 | |
396 virtual void GetButtonNames(std::string& pushButton1, | |
397 std::string& pushButton2, | |
398 std::string& tool1, | |
399 std::string& tool2 | |
400 ) { | |
401 tool1 = "line"; | |
402 tool2 = "circle"; | |
403 pushButton1 = "action1"; | |
404 pushButton2 = "action2"; | |
405 } | |
406 | |
407 #if ORTHANC_ENABLE_WASM==1 | |
408 virtual void InitializeWasm() { | |
409 | |
410 AttachWidgetToWasmViewport("canvas", thumbnailsLayout_); | |
411 AttachWidgetToWasmViewport("canvas2", mainWidget_); | |
412 } | |
413 #endif | |
414 | |
415 #if ORTHANC_ENABLE_QT==1 | |
416 virtual QStoneMainWindow* CreateQtMainWindow() { | |
417 return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this); | |
418 } | |
419 #endif | |
420 }; | |
421 | |
422 | |
423 } | |
424 } |