Mercurial > hg > orthanc-stone
annotate Applications/Samples/SimpleViewerApplication.h @ 276:5de5699ad570 am-2
first display in QCairoWidget; no mouse interaction yet
author | am@osimis.io |
---|---|
date | Mon, 27 Aug 2018 12:21:52 +0200 |
parents | dc1beee33134 |
children | 8f5d7495076d |
rev | line source |
---|---|
235 | 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. | |
249 | 16 * |
235 | 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/Widgets/LayerWidget.h" | |
28 #include "../../Framework/Widgets/LayoutWidget.h" | |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
29 #include "../../Framework/Messages/IObserver.h" |
258 | 30 #include "../../Framework/SmartLoader.h" |
235 | 31 |
32 #include <Core/Logging.h> | |
33 | |
34 namespace OrthancStone | |
35 { | |
36 namespace Samples | |
37 { | |
38 class SimpleViewerApplication : | |
249 | 39 public SampleApplicationBase, |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
40 public IObserver |
235 | 41 { |
42 private: | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
43 class ThumbnailInteractor : public IWorldSceneInteractor |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
44 { |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
45 private: |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
46 SimpleViewerApplication& application_; |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
47 public: |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
48 ThumbnailInteractor(SimpleViewerApplication& application) : |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
49 application_(application) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
50 { |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
51 } |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
52 |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
53 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
54 const ViewportGeometry& view, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
55 MouseButton button, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
56 double x, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
57 double y, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
58 IStatusBar* statusBar) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
59 { |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
60 if (button == MouseButton_Left) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
61 { |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
62 statusBar->SetMessage("trying to drag the thumbnail from " + widget.GetName()); |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
63 } |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
64 return NULL; |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
65 } |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
66 virtual void MouseOver(CairoContext& context, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
67 WorldSceneWidget& widget, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
68 const ViewportGeometry& view, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
69 double x, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
70 double y, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
71 IStatusBar* statusBar) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
72 {} |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
73 |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
74 virtual void MouseWheel(WorldSceneWidget& widget, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
75 MouseWheelDirection direction, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
76 KeyboardModifiers modifiers, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
77 IStatusBar* statusBar) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
78 {} |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
79 |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
80 virtual void KeyPressed(WorldSceneWidget& widget, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
81 char key, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
82 KeyboardModifiers modifiers, |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
83 IStatusBar* statusBar) |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
84 {}; |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
85 |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
86 }; |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
87 |
235 | 88 class Interactor : public IWorldSceneInteractor |
89 { | |
90 private: | |
91 SimpleViewerApplication& application_; | |
92 | |
93 public: | |
94 Interactor(SimpleViewerApplication& application) : | |
95 application_(application) | |
96 { | |
97 } | |
98 | |
99 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | |
100 const ViewportGeometry& view, | |
101 MouseButton button, | |
102 double x, | |
103 double y, | |
104 IStatusBar* statusBar) | |
105 { | |
106 return NULL; | |
107 } | |
108 | |
109 virtual void MouseOver(CairoContext& context, | |
110 WorldSceneWidget& widget, | |
111 const ViewportGeometry& view, | |
112 double x, | |
113 double y, | |
114 IStatusBar* statusBar) | |
115 { | |
116 if (statusBar != NULL) | |
117 { | |
118 Vector p = dynamic_cast<LayerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y); | |
119 | |
120 char buf[64]; | |
249 | 121 sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", |
235 | 122 p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); |
123 statusBar->SetMessage(buf); | |
124 } | |
125 } | |
126 | |
127 virtual void MouseWheel(WorldSceneWidget& widget, | |
128 MouseWheelDirection direction, | |
129 KeyboardModifiers modifiers, | |
130 IStatusBar* statusBar) | |
131 { | |
249 | 132 // int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1); |
235 | 133 |
249 | 134 // switch (direction) |
135 // { | |
136 // case MouseWheelDirection_Up: | |
137 // application_.OffsetSlice(-scale); | |
138 // break; | |
235 | 139 |
249 | 140 // case MouseWheelDirection_Down: |
141 // application_.OffsetSlice(scale); | |
142 // break; | |
235 | 143 |
249 | 144 // default: |
145 // break; | |
146 // } | |
235 | 147 } |
148 | |
149 virtual void KeyPressed(WorldSceneWidget& widget, | |
150 char key, | |
151 KeyboardModifiers modifiers, | |
152 IStatusBar* statusBar) | |
153 { | |
154 switch (key) | |
155 { | |
249 | 156 case 's': |
157 widget.SetDefaultView(); | |
158 break; | |
159 case 'n': | |
160 application_.NextImage(widget); | |
161 break; | |
235 | 162 |
249 | 163 default: |
164 break; | |
235 | 165 } |
166 } | |
167 }; | |
168 | |
169 | |
242 | 170 std::unique_ptr<Interactor> interactor_; |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
171 std::unique_ptr<ThumbnailInteractor> thumbnailInteractor_; |
235 | 172 LayoutWidget* mainLayout_; |
173 LayoutWidget* thumbnailsLayout_; | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
174 LayerWidget* mainWidget_; |
235 | 175 std::vector<LayerWidget*> thumbnails_; |
176 std::vector<std::string> instances_; | |
270 | 177 |
235 | 178 unsigned int currentInstanceIndex_; |
270 | 179 OrthancStone::WidgetViewport* wasmViewport1_; |
180 OrthancStone::WidgetViewport* wasmViewport2_; | |
235 | 181 |
254 | 182 IStatusBar* statusBar_; |
235 | 183 unsigned int slice_; |
258 | 184 std::unique_ptr<SmartLoader> smartLoader_; |
270 | 185 std::unique_ptr<OrthancApiClient> orthancApiClient_; |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
186 |
235 | 187 public: |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
188 SimpleViewerApplication(MessageBroker& broker) : |
258 | 189 IObserver(broker), |
235 | 190 mainLayout_(NULL), |
191 currentInstanceIndex_(0), | |
242 | 192 wasmViewport1_(NULL), |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
193 wasmViewport2_(NULL), |
258 | 194 slice_(0) |
235 | 195 { |
267 | 196 DeclareIgnoredMessage(MessageType_Widget_ContentChanged); |
197 DeclareHandledMessage(MessageType_Widget_GeometryChanged); | |
270 | 198 |
199 DeclareHandledMessage(MessageType_OrthancApi_GetStudyIds_Ready); | |
200 DeclareHandledMessage(MessageType_OrthancApi_GetStudy_Ready); | |
201 DeclareHandledMessage(MessageType_OrthancApi_GetSeries_Ready); | |
235 | 202 } |
251
192e6e349e69
first usage of new message system (in SDL only)
am@osimis.io
parents:
249
diff
changeset
|
203 |
237
b4642964c355
SimpleViewer demo running both with SDL and Wasm
am@osimis.io
parents:
235
diff
changeset
|
204 virtual void Finalize() {} |
b4642964c355
SimpleViewer demo running both with SDL and Wasm
am@osimis.io
parents:
235
diff
changeset
|
205 virtual IWidget* GetCentralWidget() {return mainLayout_;} |
b4642964c355
SimpleViewer demo running both with SDL and Wasm
am@osimis.io
parents:
235
diff
changeset
|
206 |
235 | 207 virtual void DeclareStartupOptions(boost::program_options::options_description& options) |
208 { | |
209 boost::program_options::options_description generic("Sample options"); | |
210 generic.add_options() | |
270 | 211 ("studyId", boost::program_options::value<std::string>(), |
212 "Orthanc ID of the study") | |
249 | 213 ; |
235 | 214 |
249 | 215 options.add(generic); |
235 | 216 } |
217 | |
242 | 218 virtual void Initialize(BasicApplicationContext* context, |
219 IStatusBar& statusBar, | |
235 | 220 const boost::program_options::variables_map& parameters) |
221 { | |
222 using namespace OrthancStone; | |
223 | |
242 | 224 context_ = context; |
254 | 225 statusBar_ = &statusBar; |
270 | 226 |
227 {// initialize viewports and layout | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
228 mainLayout_ = new LayoutWidget("main-layout"); |
270 | 229 mainLayout_->SetPadding(10); |
230 mainLayout_->SetBackgroundCleared(true); | |
231 mainLayout_->SetBackgroundColor(0, 0, 0); | |
232 mainLayout_->SetHorizontal(); | |
233 | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
234 thumbnailsLayout_ = new LayoutWidget("thumbnail-layout"); |
270 | 235 thumbnailsLayout_->SetPadding(10); |
236 thumbnailsLayout_->SetBackgroundCleared(true); | |
237 thumbnailsLayout_->SetBackgroundColor(50, 50, 50); | |
238 thumbnailsLayout_->SetVertical(); | |
239 | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
240 mainWidget_ = new LayerWidget(broker_, "main-viewport"); |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
241 mainWidget_->RegisterObserver(*this); |
270 | 242 |
243 // hierarchy | |
244 mainLayout_->AddWidget(thumbnailsLayout_); | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
245 mainLayout_->AddWidget(mainWidget_); |
270 | 246 |
247 // sources | |
248 smartLoader_.reset(new SmartLoader(broker_, context_->GetWebService())); | |
249 smartLoader_->SetImageQuality(SliceImageQuality_FullPam); | |
250 | |
251 mainLayout_->SetTransmitMouseOver(true); | |
252 interactor_.reset(new Interactor(*this)); | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
253 mainWidget_->SetInteractor(*interactor_); |
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
254 thumbnailInteractor_.reset(new ThumbnailInteractor(*this)); |
270 | 255 } |
256 | |
235 | 257 statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); |
254 | 258 statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport"); |
235 | 259 |
270 | 260 orthancApiClient_.reset(new OrthancApiClient(broker_, context_->GetWebService())); |
261 | |
262 if (parameters.count("studyId") < 1) | |
235 | 263 { |
270 | 264 LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc"; |
265 orthancApiClient_->ScheduleGetStudyIds(*this); | |
235 | 266 } |
270 | 267 else |
235 | 268 { |
270 | 269 SelectStudy(parameters["studyId"].as<std::string>()); |
235 | 270 } |
270 | 271 } |
235 | 272 |
270 | 273 void OnStudyListReceived(const Json::Value& response) |
274 { | |
275 if (response.isArray() && response.size() > 1) | |
276 { | |
277 SelectStudy(response[0].asString()); | |
278 } | |
279 } | |
280 void OnStudyReceived(const Json::Value& response) | |
281 { | |
282 if (response.isObject() && response["Series"].isArray()) | |
283 { | |
284 for (size_t i=0; i < response["Series"].size(); i++) | |
285 { | |
286 orthancApiClient_->ScheduleGetSeries(*this, response["Series"][(int)i].asString()); | |
287 } | |
288 } | |
289 } | |
235 | 290 |
270 | 291 void OnSeriesReceived(const Json::Value& response) |
292 { | |
293 if (response.isObject() && response["Instances"].isArray() && response["Instances"].size() > 0) | |
294 { | |
295 LoadThumbnailForSeries(response["ID"].asString(), response["Instances"][0].asString()); | |
296 } | |
297 } | |
298 | |
299 void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId) | |
300 { | |
271
46c5296d867e
OracleWebService and BasicSdlApplicationContext using the same global mutex
am@osimis.io
parents:
270
diff
changeset
|
301 LOG(INFO) << "Loading thumbnail for series " << seriesId; |
46c5296d867e
OracleWebService and BasicSdlApplicationContext using the same global mutex
am@osimis.io
parents:
270
diff
changeset
|
302 LayerWidget* thumbnailWidget = new LayerWidget(broker_, "thumbnail-series-" + seriesId); |
46c5296d867e
OracleWebService and BasicSdlApplicationContext using the same global mutex
am@osimis.io
parents:
270
diff
changeset
|
303 thumbnails_.push_back(thumbnailWidget); |
46c5296d867e
OracleWebService and BasicSdlApplicationContext using the same global mutex
am@osimis.io
parents:
270
diff
changeset
|
304 thumbnailsLayout_->AddWidget(thumbnailWidget); |
270 | 305 thumbnailWidget->RegisterObserver(*this); |
306 thumbnailWidget->AddLayer(smartLoader_->GetFrame(instanceId, 0)); | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
307 //thumbnailWidget->SetInteractor(*thumbnailInteractor_); |
270 | 308 } |
235 | 309 |
270 | 310 void SelectStudy(const std::string& studyId) |
311 { | |
312 orthancApiClient_->ScheduleGetStudy(*this, studyId); | |
313 } | |
314 | |
315 virtual void HandleMessage(IObservable& from, const IMessage& message) { | |
316 switch (message.GetType()) { | |
317 case MessageType_Widget_GeometryChanged: | |
271
46c5296d867e
OracleWebService and BasicSdlApplicationContext using the same global mutex
am@osimis.io
parents:
270
diff
changeset
|
318 LOG(INFO) << "Widget geometry ready: " << dynamic_cast<LayerWidget&>(from).GetName(); |
270 | 319 dynamic_cast<LayerWidget&>(from).SetDefaultView(); |
320 break; | |
321 case MessageType_OrthancApi_GetStudyIds_Ready: | |
322 OnStudyListReceived(dynamic_cast<const OrthancApiClient::GetJsonResponseReadyMessage&>(message).response_); | |
323 break; | |
324 case MessageType_OrthancApi_GetSeries_Ready: | |
325 OnSeriesReceived(dynamic_cast<const OrthancApiClient::GetJsonResponseReadyMessage&>(message).response_); | |
326 break; | |
327 case MessageType_OrthancApi_GetStudy_Ready: | |
328 OnStudyReceived(dynamic_cast<const OrthancApiClient::GetJsonResponseReadyMessage&>(message).response_); | |
329 break; | |
330 default: | |
331 VLOG("unhandled message type" << message.GetType()); | |
332 } | |
242 | 333 } |
237
b4642964c355
SimpleViewer demo running both with SDL and Wasm
am@osimis.io
parents:
235
diff
changeset
|
334 |
274
dc1beee33134
split SdlApplication into NativeApplication and SdlApplication
am@osimis.io
parents:
273
diff
changeset
|
335 #if ORTHANC_ENABLE_WASM==1 |
242 | 336 virtual void InitializeWasm() { |
337 | |
338 AttachWidgetToWasmViewport("canvas", thumbnailsLayout_); | |
274
dc1beee33134
split SdlApplication into NativeApplication and SdlApplication
am@osimis.io
parents:
273
diff
changeset
|
339 AttachWidgetToWasmViewport("canvas2", mainWidget_); |
242 | 340 } |
237
b4642964c355
SimpleViewer demo running both with SDL and Wasm
am@osimis.io
parents:
235
diff
changeset
|
341 #endif |
249 | 342 |
343 void NextImage(WorldSceneWidget& widget) { | |
344 assert(context_); | |
254 | 345 statusBar_->SetMessage("displaying next image"); |
249 | 346 |
347 currentInstanceIndex_ = (currentInstanceIndex_ + 1) % instances_.size(); | |
348 | |
273
f21ba2468570
force all Widgets to have a name to ease debugging
am@osimis.io
parents:
271
diff
changeset
|
349 mainWidget_->ReplaceLayer(0, smartLoader_->GetFrame(instances_[currentInstanceIndex_], 0)); |
249 | 350 |
351 } | |
235 | 352 }; |
249 | 353 |
354 | |
235 | 355 } |
356 } |