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 
9 int 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 
20 class QCheckBox;
21 class QGroupBox;
22 class QPushButton;
23 class QComboBox;
24 class QRadioButton;
25 
26 class StatisticsDisplay;
27 
30 
31 class MainWidget : public QWidget
32 {
33 
34  Q_OBJECT
35 
36 public:
37  explicit MainWidget();
38 
39  ~MainWidget();
40 
41 public 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 
81 private:
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
STL class.
View to display an image.
Definition: decl_image_view.hpp:70

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 
18 MainWidget::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"),
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 
94 MainWidget::~MainWidget()
95 {
96  // Stop the stream before destructing handlers
97  streamH_->Finish();
98 }
99 
100 void MainWidget::onGrabChanged(int state)
101 {
102  // Toggle grab
103  state == Qt::Unchecked ? streamH_->Finish() : streamH_->Run();
104 }
105 
106 void 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 
142 void 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 
165 void 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 
184 void 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 
199 void MainWidget::onPortIndexChanged(const int index)
200 {
201  // Load new device to tmp and check if exists
202  loadDiscoveredDevice(index);
203 }
204 
205 void 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 
243 void 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 
258 void 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 
346 void 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 
378 void 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 }
std::string toStdString() const const
Namespace for driver or device related operations.
Definition: decl_composite.hpp:27
Limit discovery depth to vin-driver level.
static std::unique_ptr< Image > Load(const String &fileName)
Loads an image with the given file name.
Definition: detail_image.hpp:32
AcquisitionStack
Defines the acquisition stack when opening the device.
Definition: driver.hpp:273
Count that only contains lost images due to ring buffer overflow.
std::string toStdString() const const
static std::unique_ptr< SingleStreamHandler > Create(const Driver::StreamPtr &stream)
Create a stream handler object.
Definition: decl_single_stream_handler.hpp:46
Use Vin acquisition stack or fail.
Gets how many trigger signals where ignored by the device due to e.g. over-triggering.
STL class.
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QString number(int n, int base)
QString fromStdString(const std::string &str)
Root namespace for the Image Manager interface.
Definition: version.hpp:11
Number of images currently in locked state.
T Y() const noexcept
Gets the y-component of the point.
Definition: point_2d.hpp:106
bool isEmpty() const const
Number of images acquired, but not retrieved via the stream's wait method.
void setOverrideCursor(const QCursor &cursor)
Special runtime exception to carry a native error code.
Definition: exception.hpp:137
STL class.
Number of buffer currently being filled by the acquisition engine.
T X() const noexcept
Gets the x-component of the point.
Definition: point_2d.hpp:86
std::unique_ptr< Image > Open(const Image &image, MorphologyMask maskType, Size2D< int > maskSize, Point2D< int > maskOffset)
Perform an open operation with a selectable filter mask.
Definition: morphology.hpp:209
QByteArray toLatin1() const const
String InstallPath()
Directory Common Vision Blox has been installed to.
Definition: system_info.hpp:242
For packet based protocols this contains the actual number of packets received (parts of a whole imag...
void addStretch(int stretch)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
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:50
Count that only contains lost images during transfer.
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
The number of images buffers acquired since start of the last acquisition start.
Number of resend requests sent since start of the last acquisition.
static std::vector< DiscoveryInformation > Discover()
Discovers available devices (not vins) with a default time span of 300ms.
Definition: decl_device_factory.hpp:216
void addLayout(QLayout *layout, int stretch)