CVB++ 15.0
Cvb/QtStatisticsDisplay

This example requires Qt5 >= 5.9 setup for building.

You may build it with Ubuntu 18.04's default Qt5 after installing:

The main file just shows the main widget

1// ---------------------------------------------------------------------------
4// ---------------------------------------------------------------------------
5
6#include "main_widget.hpp"
7#include <QIcon>
8
9int main(int argc, char *argv[])
10{
11 QApplication app(argc, argv);
12 app.setWindowIcon(QIcon(":/CVB_Qt_App.png"));
13 MainWidget mainWidget;
14 mainWidget.show();
15 mainWidget.resize(1024, 640);
16 return app.exec();
17}
void setWindowIcon(const QIcon &icon)

Main QWidget declaration, adding some additional controls.

1#ifndef MAINWIDGET_HPP_B6881A7A_C21F_488F_834C_2046B8CE3362
2#define MAINWIDGET_HPP_B6881A7A_C21F_488F_834C_2046B8CE3362
3
4#include <iostream>
5
6#include <QApplication>
7#include <QHBoxLayout>
8#include <QLabel>
9#include <QScopedPointer>
10#include <QSharedPointer>
11#include <QTimer>
12#include <QToolTip>
13#include <QVBoxLayout>
14#include <QWidget>
15
16#include <tuple>
17#include <cvb/async/single_stream_handler.hpp>
18#include <cvb/ui/image_view.hpp>
19
20class QCheckBox;
21class QGroupBox;
22class QPushButton;
23class QComboBox;
24class QRadioButton;
25
26class StatisticsDisplay;
27
30
31class MainWidget : public QWidget
32{
33
34 Q_OBJECT
35
36public:
37 explicit MainWidget();
38
39 ~MainWidget();
40
41public Q_SLOTS:
42
47 void onGrabChanged(int state);
48
52 void onOpen();
53
57 void onDiscover();
58
62 void onSave();
63
67 void onSnaped();
68
72 void onPortIndexChanged(const int index);
73
79 void updateStatistics();
80
81private:
86 void blockUI(const bool block);
87
92 void loadDiscoveredDevice(const unsigned long devNum);
93
97 void initializeStatistics();
98
102 void initalizeTooltip();
103
104 // RAII signal blocker
105 class SignalGuard
106 {
107 public:
108 explicit SignalGuard(MainWidget* mainWidget) : mainWidget_(mainWidget)
109 {
110 mainWidget_->blockUI(true);
111 }
112
113 ~SignalGuard() { mainWidget_->blockUI(false); }
114
115 private:
116 MainWidget* mainWidget_;
117 };
118
119 std::vector<QHBoxLayout*> gbHStatNameValLayout_;
120 QVBoxLayout* gbStatEntriesLayout_;
121
122 QPushButton* pbSnap_;
123 QCheckBox* chkGrab_;
124 QGroupBox* gbSelectedPort_;
125 QGroupBox* gbStatistics_;
126 QComboBox* cbSelectedPort_;
127
128 Cvb::UI::ImageView* view_;
129
130 Cvb::DevicePtr device_;
131 Cvb::ImagePtr img;
132 Cvb::SingleStreamHandlerPtr streamH_;
133
135
136 tuple_list statisticsList_;
137 QTimer* updateStatTimer_;
138 std::vector<QLabel*> statNames_;
139 std::vector<QLabel*> statValues_;
140 QLabel* mouseText_;
141};
142
143#endif
View to display an image.
Definition: decl_image_view.hpp:75

Main QWidget definition, using the QApplication main loop to redraw as fast as possible.

1#include "main_widget.hpp"
2
3#include <qmath.h>
4#include <QCheckBox>
5#include <QComboBox>
6#include <QDebug>
7#include <QDesktopWidget>
8#include <QFileDialog>
9#include <QGroupBox>
10#include <QMessageBox>
11#include <QMutexLocker>
12#include <QPushButton>
13#include <QRadioButton>
14#include <QWeakPointer>
15
16#include <cvb/device_factory.hpp>
17
18MainWidget::MainWidget()
19 : QWidget(),
20 gbStatEntriesLayout_(new QVBoxLayout), pbSnap_(new QPushButton("Sna&p", this)),
21 chkGrab_(new QCheckBox("&Grab", this)), gbSelectedPort_(new QGroupBox("Selected Port", this)),
22 gbStatistics_(new QGroupBox("Statistic", this)), cbSelectedPort_(new QComboBox(gbSelectedPort_)),
23 view_(new Cvb::UI::ImageView(this)), device_(Cvb::DeviceFactory::Open(Cvb::InstallPath() + CVB_LIT("drivers/CVMock.vin"),
24 Cvb::AcquisitionStack::Vin)),
25 streamH_(Cvb::Async::SingleStreamHandler::Create(device_->Stream())), updateStatTimer_(new QTimer(this))
26{
27 qDebug() << "Please wait for the setup to load...";
28
29 // Widgets we do not need to modify (only local)
30 QPushButton* pbOpen = new QPushButton("&Open Image", this);
31 QPushButton* pbOpenDev = new QPushButton("&Discover Devices", this);
32 QPushButton* pbSave = new QPushButton("&Save Image", this);
33
34 // Create layouts
35 QHBoxLayout* mainLayout = new QHBoxLayout;
36 QVBoxLayout* sideLayout = new QVBoxLayout;
37
38 QVBoxLayout* gbSelectedPortLayout = new QVBoxLayout;
39
40 // add the widgets to the side layout
41 sideLayout->addWidget(pbOpen);
42 sideLayout->addWidget(pbOpenDev);
43 sideLayout->addWidget(pbSave);
44 sideLayout->addWidget(chkGrab_);
45 sideLayout->addWidget(pbSnap_);
46
47 // Discover devices and fill list
48 onDiscover();
49
50 // Add horizontal statistics entries to stat. layout and attach to side layout
51 gbStatistics_->setLayout(gbStatEntriesLayout_);
52 sideLayout->addWidget(gbStatistics_);
53
54 // add a stretch
55 sideLayout->addStretch();
56
57 // add the combobox to the layout
58 gbSelectedPortLayout->addWidget(cbSelectedPort_);
59 // add the combobox to the group box layout
60 gbSelectedPort_->setLayout(gbSelectedPortLayout);
61 // add the group box to the side layout
62 sideLayout->addWidget(gbSelectedPort_);
63
64 // add the side layout to the main layout
65 mainLayout->addLayout(sideLayout);
66 // add a stretch
67 mainLayout->addStretch();
68 // add the display
69 mainLayout->addWidget(view_);
70 // now use this layout
71 setLayout(mainLayout);
72
73 // Connections
74 connect(pbOpen, SIGNAL(clicked()), this, SLOT(onOpen()));
75 connect(pbOpenDev, SIGNAL(clicked()), this, SLOT(onDiscover()));
76 connect(pbSave, SIGNAL(clicked()), this, SLOT(onSave()));
77 connect(pbSnap_, SIGNAL(clicked()), this, SLOT(onSnaped()));
78 connect(chkGrab_, SIGNAL(stateChanged(int)), this, SLOT(onGrabChanged(int)));
79 connect(cbSelectedPort_, SIGNAL(currentIndexChanged(int)), this, SLOT(onPortIndexChanged(const int)));
80 // Statistics timed update
81 connect(updateStatTimer_, SIGNAL(timeout()), SLOT(updateStatistics()));
82 updateStatTimer_->start(100);
83
84 // Second image view for stream handler to test
85 view_->SetUploadMode(Cvb::UI::UploadMode::Viewport);
86 view_->SetRenderEngine(Cvb::UI::RenderEngine::Raster);
87 view_->Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
88 view_->SetWaitForRepaintEnabled(false);
89
90 // Add statistics text
91 initalizeTooltip();
92}
93
94MainWidget::~MainWidget()
95{
96 // Stop the stream before destructing handlers
97 streamH_->Finish();
98}
99
100void MainWidget::onGrabChanged(int state)
101{
102 // Toggle grab
103 state == Qt::Unchecked ? streamH_->Finish() : streamH_->Run();
104}
105
106void MainWidget::onOpen()
107{
108 // Block all possibly recursive signals and update the UI according to the current image
109 SignalGuard guard(this);
110
111 try
112 {
113 // Stop grab if running
114 if (streamH_->IsActive())
115 {
116 streamH_->Finish();
117 chkGrab_->setChecked(false);
118 }
119
120 // Open file dialog
122 this, tr("Open Image"), QString("/"), tr("CImages (*.bmp *.jpg *.png *.tif *.tiff)"));
123
124 if (!filename.isEmpty())
125 {
126
127 // Reset old image if it exists
128 if (img)
129 img.reset();
130
131 // Load new image
132 img = Cvb::Image::Load(filename.toStdString());
134 }
135 }
136 catch (const Cvb::CvbException& e)
137 {
138 std::cout << e.what() << "\nError opening image!" << std::endl;
139 }
140}
141
142void MainWidget::onDiscover()
143{
144 SignalGuard guard(this);
145 std::cout << "Discovering devices..." << std::endl;
146
148 cbSelectedPort_->clear();
149 gbSelectedPort_->setEnabled(true);
150 for (const auto& device : discover_)
151 {
152 Cvb::String devModel{ "Unset" };
153
154 // Write device model name to list if available, else number each entry
155 if (device.TryGetProperty(Cvb::Driver::DiscoveryProperties::DeviceModel, devModel))
156 cbSelectedPort_->addItem(QString::fromStdString(devModel), static_cast<int32_t>(device.Index()));
157 else
158 cbSelectedPort_->addItem(QString("Device %1").arg(device.Index()), static_cast<int32_t>(device.Index()));
159 }
160 cbSelectedPort_->setCurrentIndex(0);
161
162 loadDiscoveredDevice(0ul); // Load first device
163}
164
165void MainWidget::onSave()
166{
167 // Block all possibly recursive signals and update the UI according to the current image
168 SignalGuard guard(this);
169
170 // Stop grab if running
171 if (streamH_->IsActive())
172 {
173 streamH_->Finish();
174 chkGrab_->setChecked(false);
175 }
176
177 // Open file dialog
179 this, tr("Save Image"), QString("~/"), tr("CImages (*.bmp *.jpg *.png *.tif *.tiff)"));
180 if (!filename.isEmpty())
181 view_->Image()->Save(filename.toLatin1().toStdString());
182}
183
184void MainWidget::onSnaped()
185{
186 // Stop grab if running
187 if (streamH_->IsActive())
188 {
189 streamH_->Finish();
190 chkGrab_->setChecked(false);
191 }
192
193 auto waitResult = device_->Stream()->GetSnapshot();
194
195 if (waitResult.Image == nullptr)
196 throw std::runtime_error("Could not snap image.");
197}
198
199void MainWidget::onPortIndexChanged(const int index)
200{
201 // Load new device to tmp and check if exists
202 loadDiscoveredDevice(index);
203}
204
205void MainWidget::loadDiscoveredDevice(const unsigned long devNum)
206{
207 SignalGuard guard(this);
208 std::cout << "Loading device..." << std::endl;
209
210 // Stop grab if running
211 if (streamH_->IsActive())
212 {
213 streamH_->Finish();
214 chkGrab_->setChecked(false);
215 }
216 // Load first discovered device to tmp and check if exists
217 try
218 {
219 Cvb::DevicePtr tmpDevPtr{ Cvb::DeviceFactory::Open(discover_.at(devNum).AccessToken(), Cvb::AcquisitionStack::Vin) };
220
221 if (tmpDevPtr)
222 {
223 std::cout << "Loading device " << discover_.at(devNum).AccessToken() << std::endl;
224
225 // Reset stream and device pointer
226 streamH_.reset();
227 device_.reset();
228
229 device_ = tmpDevPtr;
230 streamH_ = Cvb::SingleStreamHandler::Create(device_->Stream());
231
232 view_->Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
233
234 initializeStatistics();
235 }
236 }
237 catch (const Cvb::CvbException& e)
238 {
239 std::cout << e.what() << "\nError changing discovered device!" << std::endl;
240 }
241}
242
243void MainWidget::updateStatistics()
244{
245 SignalGuard guard(this);
246 unsigned long currentStat = 0;
247 for (auto const& stat : statisticsList_)
248 {
249 // Update available statistic values
250 auto const statEnumName = std::get<0>(stat);
251 auto value = device_->Stream()->Statistics(statEnumName);
252 auto statValue = QString::number(value);
253 statValues_.at(currentStat)->setText(statValue);
254 ++currentStat;
255 }
256}
257
258void MainWidget::initializeStatistics()
259{
260 std::cout << "Creating device statistic..." << std::endl;
261
262 using namespace Cvb::Driver;
263
264 // Clean statistics in case of repaint after device change
265 for (auto& valWdg : statValues_)
266 {
267 gbStatEntriesLayout_->removeWidget(valWdg);
268 delete valWdg;
269 }
270 for (auto& nameWdg : statNames_)
271 {
272 gbStatEntriesLayout_->removeWidget(nameWdg);
273 delete nameWdg;
274 }
275 for (auto& lay : gbHStatNameValLayout_)
276 {
277 // delete lay;
278 gbStatEntriesLayout_->removeItem(lay);
279 delete lay;
280 }
281
282 // Set displayable statistics here
283 statisticsList_.clear();
284 statisticsList_.push_back(tuple_element(StreamInfo::IsCameraDetected, "IsCameraDetected"));
285 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersAcquired, "NumBuffersAcquired"));
286 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersAnnounced, "NumBuffersAnnounced"));
287 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersPending, "NumBuffersPending"));
288 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersBeingFilled, "NumBuffersBeingFilled"));
289 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersDelivered, "NumBuffersDelivered"));
290 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersLocked, "NumBuffersLocked"));
291 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersLost, "NumBuffersLost"));
292 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersLostLocked, "NumBuffersLostLocked"));
293 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersLostTransfer, "NumBuffersLostTransfer"));
294 statisticsList_.push_back(tuple_element(StreamInfo::NumBuffersQueued, "NumBuffersQueued"));
295 statisticsList_.push_back(tuple_element(StreamInfo::NumPacketsReceived, "NumPacketsReceived"));
296 statisticsList_.push_back(tuple_element(StreamInfo::NumResends, "NumResends"));
297 statisticsList_.push_back(tuple_element(StreamInfo::NumTriggersLost, "NumTriggersLost"));
298
299 // Fill out statistic display
300 statNames_.clear();
301 statValues_.clear();
302 gbHStatNameValLayout_.clear();
303 unsigned long currentStat = 0;
304 int unavailableStatisticPos = -1;
305 std::vector<int> delEntries{};
306 for (auto const& stat : statisticsList_)
307 {
308 try
309 {
310 ++unavailableStatisticPos;
311 // Set available statistic values
312 auto const statEnumName = std::get<0>(stat);
313 auto value = device_->Stream()->Statistics(statEnumName); // May throw exception when statistic is not available
314 std::string statValue = std::to_string(value);
315 statValues_.push_back(new QLabel(tr(statValue.c_str())));
316 statValues_.back()->setAlignment(Qt::AlignLeft);
317 statValues_.back()->updatesEnabled();
318
319 // Set available statistic names
320 statNames_.push_back(new QLabel(tr(std::get<1>(stat).c_str())));
321
322 // Add statistic name and value to horizontal layout
323 gbHStatNameValLayout_.push_back(new QHBoxLayout);
324 gbHStatNameValLayout_.at(currentStat)->addWidget(statNames_.back());
325 statValues_.back()->setAlignment(Qt::AlignRight);
326 gbHStatNameValLayout_.at(currentStat)->addWidget(statValues_.back());
327 // Add horizontal layout to vertical statistics layout
328 gbStatEntriesLayout_->addLayout(gbHStatNameValLayout_.at(currentStat));
329 ++currentStat;
330 }
331 catch (const std::exception& e)
332 {
333 std::cout << "Removing statistic due to error: " << e.what() << std::endl;
334 // Store vector position of unavailable statistic
335 delEntries.push_back(unavailableStatisticPos);
336 }
337 }
338
339 // Remove unavailable statistics from statistic list
340 for (auto ri = delEntries.rbegin(); ri != delEntries.rend(); ++ri)
341 statisticsList_.erase(statisticsList_.begin() + *ri);
342
343 std::cout << "Done initializing" << std::endl;
344}
345
346void MainWidget::initalizeTooltip()
347{
348 // Mouse position information
349 view_->setMouseTracking(true);
350 QApplication::setOverrideCursor(Qt::ArrowCursor);
351
352 // Show mouse position and pixel data
353 view_->RegisterEventMouseMoved([&](Cvb::Point2D<int> mousePos, const std::vector<double>& pixelVal) {
354 Cvb::StringStream titleStream;
355
356 // Check if Image is RGB like or mono
357 if (pixelVal.size() == 3)
358 {
359 titleStream << "(X:" << mousePos.X() << ", Y:" << mousePos.Y() << ") (R:" << qFloor(pixelVal[0])
360 << ", G:" << qFloor(pixelVal[1]) << ", B:" << qFloor(pixelVal[2]) << ")";
361 }
362 else if (pixelVal.size() == 1)
363 {
364 titleStream << "(X:" << mousePos.X() << ", Y:" << mousePos.Y() << ") (Mono:" << qFloor(pixelVal[0]) << ")";
365 }
366 else
367 {
368 titleStream << "(X:" << mousePos.X() << ", Y:" << mousePos.Y() << ") (Mono:";
369 for (auto& pix : pixelVal)
370 titleStream << qFloor(pix) << " | ";
371 titleStream << ")";
372 }
373
374 view_->setToolTip(QString::fromStdString(titleStream.str()));
375 });
376}
377
378void MainWidget::blockUI(const bool block)
379{
380 chkGrab_->blockSignals(block);
381 pbSnap_->blockSignals(block);
382 cbSelectedPort_->blockSignals(block);
383 view_->blockSignals(block);
384 gbSelectedPort_->blockSignals(block);
385 gbStatistics_->blockSignals(block);
386 updateStatTimer_->blockSignals(block);
387}
static std::unique_ptr< SingleStreamHandler > Create(const Driver::StreamPtr &stream)
Create a stream handler object.
Definition: decl_single_stream_handler.hpp:46
Special runtime exception to carry a native error code.
Definition: exception.hpp:139
static std::vector< DiscoveryInformation > Discover()
Discovers available devices (not vins) with a default time span of 300ms.
Definition: decl_device_factory.hpp:221
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
Opens a device with the given provider with its default board and port (if applicable).
Definition: decl_device_factory.hpp:55
static std::unique_ptr< Image > Load(const String &fileName)
Loads an image with the given file name.
Definition: detail_image.hpp:32
Namespace for driver or device related operations.
Definition: decl_composite.hpp:28
@ DeviceModel
Device only : Model name.
@ UpToLevelVin
Limit discovery depth to vin-driver level.
Root namespace for the Image Manager interface.
Definition: c_barcode.h:24
void addLayout(QLayout *layout, int stretch)
void addStretch(int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
std::string toStdString() const const
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
void setOverrideCursor(const QCursor &cursor)
QString fromStdString(const std::string &str)
bool isEmpty() const const
QString number(int n, int base)
QByteArray toLatin1() const const
std::string toStdString() const const