CVB++ 15.0
detail_image_view.hpp
1#pragma once
2
3#include "../../global.hpp"
4#include "../../rect.hpp"
5
6#include "../../driver/_decl/decl_device_image.hpp"
7
8#include "../_decl/decl_image_scene.hpp"
9#include "../_decl/decl_image_view.hpp"
10#include "../_decl/decl_opengl_image_renderer_factory.hpp"
11
12namespace Cvb
13{
14
15 CVB_BEGIN_INLINE_NS
16
17 namespace UI
18 {
19
20 inline ImageView::ImageView(QWidget *parent)
21 : QGraphicsView(parent)
22 , zoomHandler_(*this)
23 , waitForRepaintEnabled_(true)
24 {
25 setScene(new Cvb::UI::ImageScene(this)); // NOLINT(cppcoreguidelines-owning-memory)
26
27 setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
28 setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
29 setDragMode(QGraphicsView::ScrollHandDrag);
30 setFrameShadow(QFrame::Plain);
31
32 auto refreshEventFilter = new RefreshEventFilter(this); // NOLINT(cppcoreguidelines-owning-memory)
33 installEventFilter(refreshEventFilter);
34 }
35
36 inline ImageView::ImageView(class ImageScene *scene, QWidget *parent)
37 : QGraphicsView(parent)
38 , zoomHandler_(*this)
39 , refreshMode_(Cvb::UI::RefreshMode::UploadAndWaitForRepaint)
40 , uploadMode_(Cvb::UI::UploadMode::Image)
41 , waitForRepaintEnabled_(true)
42 {
43 if (scene && scene->views().size())
44 throw std::runtime_error("a image view must be the only view on the scene");
45
46 setScene(scene);
47
48 setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
49 setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
50 setDragMode(QGraphicsView::ScrollHandDrag);
51 setFrameShadow(QFrame::Plain);
52
53 auto refreshEventFilter = new RefreshEventFilter(this); // NOLINT(cppcoreguidelines-owning-memory)
54 installEventFilter(refreshEventFilter);
55 }
56
57 inline ImageView::~ImageView()
58 {
60 }
61
63 {
64 std::unique_lock<std::mutex> guard(imageMutex_);
65 if (!image_)
66 throw std::runtime_error("no image shared");
67 Refresh(*image_, refreshMode);
68 }
69
70 inline void ImageView::Refresh(const ImagePtr &image, Cvb::UI::RefreshMode refreshMode, AutoRefresh autoRefresh)
71 {
72 if (!image)
73 throw std::runtime_error("refresh with empty image pointer");
74
75 std::unique_lock<std::mutex> guard(imageMutex_);
76 UnregisterEventImageDataUpdated();
77 image_ = image;
78
79 if (autoRefresh == AutoRefresh::On)
80 eventCookieDataUpdated_ =
81 image_->RegisterEventPixelContentChanged([&](const class Image &, Rect<int>) { Refresh(); });
82
83 Refresh(*image_, refreshMode);
84 }
85
86 inline void ImageView::Refresh(const class Image &image, Cvb::UI::RefreshMode refreshMode)
87 {
88 std::unique_lock<std::mutex> guard(refreshMutex_);
89 if (!image_ && uploadMode_ == Cvb::UI::UploadMode::Viewport)
90 throw std::runtime_error("an image must be shared with the display to upload the viewport only");
91
92 auto imageScene = ImageScene();
93 imageScene->UploadImage(image);
94 zoomHandler_.UpdatePanoramaFactor();
95
96 switch (refreshMode)
97 {
98 default:
100 break;
101
103 WaitForRepaint();
104 break;
105
107 ScheduleRepaint();
108 break;
109 }
110 }
111
112 inline void ImageView::SetWaitForRepaintEnabled(bool enabled)
113 {
114 if (qApp->thread() != QThread::currentThread()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
115 throw std::runtime_error("repaint can only be enabled or disabled form the UI thread");
116
117 if (enabled)
118 waitForRepaintEnabled_ = true;
119 else
120 {
121 {
122 std::unique_lock<std::mutex> guard(waitMutex_);
123 waitForRepaintEnabled_ = false;
124 }
125 QApplication::removePostedEvents(this, RefreshEventType());
126 waitCondition_.notify_all();
127 }
128 }
129
130 inline void ImageView::CustomWaitForRepaint(std::function<void()> customWaitForRepaint) noexcept
131 {
132 std::unique_lock<std::mutex> guard(imageMutex_);
133 waitForRepaint_ = customWaitForRepaint;
134 }
135
137 {
138 std::unique_lock<std::mutex> guard(imageMutex_);
139 waitForRepaint_ = [&]() { viewport()->repaint(); };
140 }
141
142 inline void ImageView::CustomScheduleRepaint(std::function<void()> customScheduleRepaint) noexcept
143 {
144 std::unique_lock<std::mutex> guard(imageMutex_);
145 scheduleRepaint_ = customScheduleRepaint;
146 }
147
149 {
150 std::unique_lock<std::mutex> guard(imageMutex_);
151 scheduleRepaint_ = [&]() { update(); };
152 }
153
154 inline void ImageView::ReleaseRefreshShare() noexcept
155 {
156 std::unique_lock<std::mutex> guard(imageMutex_);
157 UnregisterEventImageDataUpdated();
158 image_.reset();
159 }
160
161 inline ImageScene *ImageView::ImageScene() const noexcept
162 {
163 return dynamic_cast<class ImageScene *>(scene());
164 }
165
166 inline bool ImageView::TryZoomIn() noexcept
167 {
168 try
169 {
170 auto waitForRepaint = IsWaitForRepaintEnabled();
172 auto result = zoomHandler_.TryZoomIn();
173
174 if (result && uploadMode_ == Cvb::UI::UploadMode::Viewport && image_ && eventRefreshEnabled_)
176 else if (result && eventRefreshEnabled_)
177 {
178 zoomHandler_.UpdatePanoramaFactor();
179 viewport()->update();
180 }
181
182 SetWaitForRepaintEnabled(waitForRepaint);
183 return result;
184 }
185 catch (...)
186 {
187 return false; // ensure noexcept
188 }
189 }
190
191 inline bool ImageView::TryZoomOut() noexcept
192 {
193 try
194 {
195 auto waitForRepaint = IsWaitForRepaintEnabled();
197 auto result = zoomHandler_.TryZoomOut();
198
199 if (result && uploadMode_ == Cvb::UI::UploadMode::Viewport && image_ && eventRefreshEnabled_)
201 else if (result && eventRefreshEnabled_)
202 {
203 zoomHandler_.UpdatePanoramaFactor();
204 viewport()->update();
205 }
206
207 SetWaitForRepaintEnabled(waitForRepaint);
208 return result;
209 }
210 catch (...)
211 {
212 return false; // ensure noexcept
213 }
214 }
215
217 {
218 ImageScene()->SetRenderEngine(renderEngine);
219 }
220
221 inline EventCookie
223 {
224 auto carrier = Internal::CbCarrier<void(Point2D<int>, const std::vector<double> &)>::Create(handler);
225 return mouseMovedCarrierContainer_.Register(carrier);
226 }
227
228 inline void ImageView::UnregisterEventMouseMoved(EventCookie eventCookie) noexcept
229 {
230 mouseMovedCarrierContainer_.Unregister(eventCookie);
231 }
232
233 inline void ImageView::closeEvent(QCloseEvent *event)
234 {
235 auto waitForRepaint = IsWaitForRepaintEnabled();
238 QGraphicsView::closeEvent(event);
239 SetWaitForRepaintEnabled(waitForRepaint);
240 }
241
242 inline void ImageView::scrollContentsBy(int dx, int dy)
243 {
244 auto waitForRepaint = IsWaitForRepaintEnabled();
246 QGraphicsView::scrollContentsBy(dx, dy);
247 if (uploadMode_ == Cvb::UI::UploadMode::Viewport && image_ && eventRefreshEnabled_)
249 else if (eventRefreshEnabled_)
250 {
251 zoomHandler_.UpdatePanoramaFactor();
252 viewport()->update();
253 }
254 SetWaitForRepaintEnabled(waitForRepaint);
255 }
256
257 inline void ImageView::resizeEvent(QResizeEvent *event)
258 {
259 auto waitForRepaint = IsWaitForRepaintEnabled();
261 QGraphicsView::resizeEvent(event);
262 zoomHandler_.UpdatePanoramaFactor();
263 if (uploadMode_ == Cvb::UI::UploadMode::Viewport && image_ && eventRefreshEnabled_)
265 else if (eventRefreshEnabled_)
266 {
267 zoomHandler_.UpdatePanoramaFactor();
268 viewport()->update();
269 }
270 SetWaitForRepaintEnabled(waitForRepaint);
271 }
272
273 inline void ImageView::showEvent(QShowEvent *event)
274 {
275 auto waitForRepaint = IsWaitForRepaintEnabled();
277 QGraphicsView::showEvent(event);
278 if ((uploadMode_ == Cvb::UI::UploadMode::Viewport && image_ && eventRefreshEnabled_)
279 || (RenderEngine() != Cvb::UI::RenderEngine::Raster && image_ && eventRefreshEnabled_))
281 else if (eventRefreshEnabled_)
282 viewport()->update();
283 SetWaitForRepaintEnabled(waitForRepaint);
284 }
285
286 inline void ImageView::wheelEvent(QWheelEvent *event)
287 {
288 auto numDegrees = event->angleDelta().y();
289
290 if (numDegrees <= -15)
291 TryZoomOut();
292 else if (numDegrees >= 15)
293 TryZoomIn();
294
295 event->accept();
296 }
297
298 inline void ImageView::mouseMoveEvent(QMouseEvent *event)
299 {
300 QGraphicsView::mouseMoveEvent(event);
301
302 auto waitForRepaint = IsWaitForRepaintEnabled();
305 Point2D<int> pixelPos;
306 {
307 std::unique_lock<std::mutex> guard(imageMutex_);
308 if (image_)
309 {
310 auto scenePos = mapToScene(mapFromGlobal(event->globalPos()));
311 pixelPos = Point2D<int>(qMin(qMax(qFloor(scenePos.x()), 0), image_->Width() - 1),
312 qMin(qMax(qFloor(scenePos.y()), 0), image_->Height() - 1));
313
314 pixel = image_->GetPixel(pixelPos);
315 }
316 }
317 SetWaitForRepaintEnabled(waitForRepaint);
318
319 if (image_)
320 mouseMovedCarrierContainer_.Call<void(Point2D<int>, const std::vector<double> &)>(pixelPos, pixel);
321 }
322
323 inline void ImageView::WaitForRepaint()
324 {
325 if (qApp->thread() == QThread::currentThread()) // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
326 waitForRepaint_();
327 else
328 {
329 std::unique_lock<std::mutex> guard(waitMutex_);
330 qApp->postEvent(this, new QEvent(RefreshEventType()), Qt::LowEventPriority); // NOLINT
331 if (waitForRepaintEnabled_)
332 waitCondition_.wait(guard);
333 }
334 }
335
336 inline void ImageView::ScheduleRepaint()
337 {
338 scheduleRepaint_();
339 }
340
341 inline bool ImageView::RefreshEventFilter::eventFilter(QObject *obj, QEvent *event)
342 {
343 if (event->type() == RefreshEventType())
344 {
345 auto imageView = dynamic_cast<ImageView *>(obj);
346 imageView->PrivateRefresh();
347 return true;
348 }
349 else
350 return QObject::eventFilter(obj, event);
351 }
352
353 inline QEvent::Type ImageView::RefreshEventType() noexcept
354 {
355 static auto refreshEventType = static_cast<QEvent::Type>(QEvent::registerEventType(QEvent::User));
356 return refreshEventType;
357 }
358
359 inline void ImageView::PrivateRefresh()
360 {
361 {
362 std::unique_lock<std::mutex> guard(waitMutex_);
363 waitForRepaint_();
364 }
365 // must be outside lock (gcc)
366 waitCondition_.notify_all();
367 }
368
369 inline void ImageView::UnregisterEventImageDataUpdated() noexcept
370 {
371 if (image_)
372 image_->UnregisterEventPixelContentChanged(eventCookieDataUpdated_);
373 }
374
375 } // namespace UI
376
377 CVB_END_INLINE_NS
378
379} // namespace Cvb
Multi-purpose 2D vector class.
Definition point_2d.hpp:20
Rectangle object.
Definition rect.hpp:24
Scene to provide a convenient display for an image.
Definition decl_image_scene.hpp:49
ImageView(QWidget *parent=nullptr)
Create an image view.
Definition detail_image_view.hpp:20
bool TryZoomIn() noexcept
Tries to zoom in.
Definition detail_image_view.hpp:166
Cvb::EventCookie RegisterEventMouseMoved(std::function< void(Cvb::Point2D< int >, const std::vector< double > &)> handler)
Register a callback to get pixel values under the moving mouse.
Definition detail_image_view.hpp:222
void ResetCustomScheduleRepaint() noexcept
Reset to the default value.
Definition detail_image_view.hpp:148
void showEvent(QShowEvent *event) override
Handle show events.
Definition detail_image_view.hpp:273
void CustomWaitForRepaint(std::function< void()> customWaitForRepaint) noexcept
Set a custom callback associated with a refresh mode.
Definition detail_image_view.hpp:130
void SetWaitForRepaintEnabled(bool enabled)
Enable and disables waiting for repaints.
Definition detail_image_view.hpp:112
void mouseMoveEvent(QMouseEvent *event) override
Handle mouse move events.
Definition detail_image_view.hpp:298
void closeEvent(QCloseEvent *event) override
Handles close events.
Definition detail_image_view.hpp:233
Cvb::UI::RenderEngine RenderEngine() const noexcept
Get the current render engine.
Definition decl_image_view.hpp:406
Cvb::UI::RefreshMode RefreshMode() const noexcept
Get the current refresh mode.
Definition decl_image_view.hpp:301
void resizeEvent(QResizeEvent *event) override
Handle resize events.
Definition detail_image_view.hpp:257
void ResetCustomWaitForRepaint() noexcept
Reset to the default value.
Definition detail_image_view.hpp:136
bool IsWaitForRepaintEnabled() const noexcept
Checks if waiting for repaint is set.
Definition decl_image_view.hpp:261
void UnregisterEventMouseMoved(Cvb::EventCookie eventCookie) noexcept
Unregister a callback to get pixel values under the moving mouse.
Definition detail_image_view.hpp:228
void wheelEvent(QWheelEvent *event) override
Handle mouse wheel events to zoom.
Definition detail_image_view.hpp:286
void Refresh()
Refresh the view.
Definition decl_image_view.hpp:113
void SetRenderEngine(Cvb::UI::RenderEngine renderEngine)
Set the current render engine.
Definition detail_image_view.hpp:216
class ImageScene * ImageScene() const noexcept
Get the image scene.
Definition detail_image_view.hpp:161
void scrollContentsBy(int dx, int dy) override
Handles scrolling the view.
Definition detail_image_view.hpp:242
bool TryZoomOut() noexcept
Tries to zoom out.
Definition detail_image_view.hpp:191
Cvb::UI::UploadMode UploadMode() const noexcept
Get the current upload mode.
Definition decl_image_view.hpp:321
ImagePtr Image() const noexcept
Get the currently shared image.
Definition decl_image_view.hpp:343
void ReleaseRefreshShare() noexcept
Releases the shared image.
Definition detail_image_view.hpp:154
void CustomScheduleRepaint(std::function< void()> scheduleRepaint) noexcept
Set a custom callback associated with a refresh mode.
Definition detail_image_view.hpp:142
Namespace for user interface components.
Definition decl_image_scene.hpp:39
RefreshMode
Defines the refresh behavior.
Definition ui.hpp:80
@ UploadAndWaitForRepaint
Definition ui.hpp:100
@ UploadAndScheduleRepaint
Definition ui.hpp:94
@ UploadOnly
Definition ui.hpp:85
AutoRefresh
Allows to automatically refresh, if image content changes.
Definition detail_ui.hpp:114
@ On
Definition detail_ui.hpp:121
@ Viewport
Definition detail_ui.hpp:53
RenderEngine
Defines the render engine used for drawing.
Definition ui.hpp:108
@ Raster
Definition ui.hpp:114
Root namespace for the Image Manager interface.
Definition c_bayer_to_rgb.h:17
std::shared_ptr< Image > ImagePtr
Convenience shared pointer for Image.
Definition global.hpp:86