Qt showing live image, escape from massive signals - c++

I have a QThread running, trying to decode image from a camera:
struct ImageQueue
{
enum {NumItems = 5};
tbb::concurrent_bounded_queue<DecodedImage> camera_queue_; // decoded image
tbb::concurrent_bounded_queue<DecodedImage> display_queue_; // widget display image
ImageQueue(int camid){camera_queue_.set_capacity(NumItems);display_queue_.set_capacity(NumItems)}
};
std::shared_ptr<ImageQueue> camera_queue;
void Worker::process()
{
while(1)
{
if(quit_)
break;
DecodedImage tmp_img;
camera_queue->camera_queue_.pop(tmp_img);
camera_queue->display_queue_.push(tmp_img);
emit imageReady();
}
emit finished();
}
And this thread is part of the Camera Class:
void Camera::Start()
{
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Camera::operate, work_, &Worker::process);
QObject::connect(this, &Camera::stopDecode, work_, &Worker::stop);
QObject::connect(work_, &Worker::imageReady, this, &Camera::DisplayImage);
workerThread_->start();
emit operate();
}
On the widget display side:
class CVImageWidget : public QGraphicsView
{
Q_OBJECT
public:
void display(DecodedImage& tmp_img);
~CVImageWidget();
private:
QGraphicsScene *scene_;
};
CVImageWidget widget;
void Camera::DisplayImage()
{
if(camera_queue != nullptr)
{
DecodedImage tmp_img;
camera_queue->display_queue_.pop(tmp_img);
widget->display(tmp_img);
}
}
void CVImageWidget::display(DecodedImage& tmp_img)
{
if(!tmp_img.isNull())
{
scene_->clear();
scene_->addPixmap(QPixmap::fromImage(tmp_img));
}
}
My question is:
Is there a way to save me from the massive imageReady signals ? I use this signal to display image because the image has to be shown in main thread otherwise the image will not be displayed. But the massive amount of signals will make the GUI response become slower.
Is there a way to fix that ? Thanks.

You do not want to be altering a graphics scene each time an image arrives. You can just as well use a QLabel and its setPixmap method, or a simple custom widget.
There is no need for a display queue: you're only interested in the most recent frame. If the UI thread lags behind the camera thread, you want to drop obsolete frames.
You have to rethink if the camera_queue needs to be a concurrent queue, since you access it from a single thread only, and whether you need that queue at all.
The typical image producer-consumer would work as follows: a Camera class interfaces with a camera and emits a hasFrame(const DecodedImage &) signal each time it got a new frame. No need for a queue: any listening object thread's event queues are concurrent queues already.
A display widget simply accepts the images to display:
class Display : public QWidget {
Q_OBJECT
DecodedImage m_image;
protected:
void paintEvent(QPaintEvent *event) {
QPainter painter{this};
painter.drawImage(0, 0, m_image);
}
public:
Display(QWidget *parent = nullptr) : QWidget{parent} {}
QSize sizeHint() const override { return m_image.size(); }
Q_SLOT void setImage(const QImage & image) {
auto oldSize = m_image.size();
m_image = image;
if (oldSize != m_image.size())
updateGeometry();
update();
}
};
You then connect the image source to the display widget and you're done. The paintEvent or other GUI thread operations can take as long as you wish, even if the camera produces images much faster than the display can consume them, you'll still show the most recent image each time the widget gets a chance to repaint itself. See this answer for a demonstration of that fact. See this answer for a similar demonstration that works on a fixed-pipeline OpenGL backend and uses the GPU to scale the image.

Mutex approach should be faster than emiting Qt signals. I've once tried to skip Qt signals by using STL condition_variable and it worked just fine for me.

Related

Image Streaming using QGraphicsView

I am trying to display images from a CameraLink Camera into a QGraphicsView.
I am using a ring buffer which get() and set() functions and two threads that consume or produce the images in this ring buffer.
This part works because I already had a successfull streaming using a QWidget. I want to use a QGraphicsView because I want to draw over those incoming images
I have a display class which is promoted to QGraphicsView, everytime an image is produced, a signal is emitted from the consumer thread to the following function:
void display::drawImage(ImageBW* image)
{
firstImage = image;
QRect rect;
rect.setHeight(1000);
rect.setWidth(2000);
QApplication::postEvent(this->viewport(),new QPaintEvent(rect));
}
I have reimplemented the painEvent of the QGraphicsview class:
void display::paintEvent(QPaintEvent *event)
{
if(firstImage)
_drawImage();
event->setAccepted(true);
}
and the drawing function is:
void display::_drawImage()
{
int w, h;
if(img)
{
delete img;
img = NULL;
}
w = firstImage->getSizeX();
h = firstImage->getSizeY();
unsigned char * udata = (unsigned char *)firstImage ->getData();
img = new QImage(udata,w, h,QImage::Format_Grayscale8);
pix_img = QPixmap::fromImage(*img);
scene->clear();
scene->addPixmap(pix_img);
scene->setSceneRect(pix_img.rect());
scene->update();
update(); // I have tried here also setScene(scene)
}
In the above code, firstImage is an ImageBW* variable. ImageBW is where the image data from camera is stored.
I have also tried with QGraphicsItem and addItem() function but i get no Image.
'scene' is initialized on the producer of this class and also the 'setScene(scene);' command is there.
The program stucks after a while so that probably means that this code produces stackoverflow.
Please I have tried everything i can think of, any help would be greatly appreciated.
Thanks in advance

Qt GraphicsScene XOR Line or Line in separate layer?

I have started to learn Qt, and try to improve my basic C++ skills.
In GraphicsScene, I try to draw a line by using the mouse (mouse events).
When I start drawing a line in GraphicsScene, a thin dashed line is drawn from the origin, where I clicked first to the current mouse position and moves with the mouse, before the second point is clicked. To erase it, I draw it in black. If I hover over already draw lines, you will see the black drawn lines on them. To undraw it without leaving marks, an XOR operation on GraphicsScene would come in handy, or if I could draw in a different layer and not touching the other layer could be handy. But I couldn't yet figure how to do it. The example is on https://github.com/JackBerkhout/QT_Draw001
In line.cpp is the function setLineP2(int x, int y), which draws and erases that thin dashed line.
Can anybody help me with this, please?
The major misconception is thinking of a QGraphicsScene as some sort of a bitmap: it's not! It is a collection of items that can render themselves, and a spatial index for them. In a scene, if you wish to delete/hide something, you must not overpaint it - instead simply delete/hide the item in question as desired. The scene will handle all the details - that's what it's for
You must forget about GDI-anything at this point. You're not painting on the raw DC here. Even when using raw GDI, you do not want to paint on the window's DC as that flickers, you should paint on a bitmap and blit the bitmap to the window.
For example, your eraseScene method adds a rectangle on top of the scene, wasting memory and resources as all the previous items are retained (you can iterate through them), whereas all it should do is to clear the scene (or its equivalent):
void MainWindow::eraseScreen(void)
{
[...]
scene->addRect(0, 0, width()+1000, height()+1000, pen, brush);
}
vs. the correct:
void MainWindow::eraseScreen(void)
{
scene->clear();
}
Below is a complete example that approximates what you presumably meant to do in your code. It is 120 lines long. It was somewhat hard to figure out what exactly you meant to do as your code is so convoluted - it's useful to describe the exact behavior in simple terms in the question.
The example uses QPainterPath to keep a list of MoveTo and LineTo elements that a QPainterPathItem renders. It also uses a QGraphicsLineItem to display the transient line.
The MyScene::PathUpdater is used to enclose the context where a path is modified, and ensure that proper pre- and post-conditions are maintained. Namely:
Since QPainterPath is implicitly shared, you should clear the path held by QGraphicsPathItem to avoid an unnecessary implicit copy. That's the precondition necessary before modifying m_path.
After m_path has been modified, the path item must be updated, as well as a new status emitted.
The following other points are worth noting:
Holding the members by value leads to a notable absence of any memory management code (!) - the compiler does it all for us. You won't find a single new or delete anywhere. They are not necessary, and we're paying no additional cost for not doing this manually. Modern C++ should look exactly like this.
The clear split between the display MainWindow and MyScene. The MainWindow knows nothing about the specifics of MyScene, and vice-versa. The code within main acts as an adapter between the two.
The leveraging of C++11.
Succinct style necessary for SO test cases and examples: for learning it's best to keep it all in one file to easily see all the parts of the code. It's only 120 lines, vs. more than twice that if split across files. Our brains leverage the locality of reference. By splitting the code you're making it harder for yourself to comprehend.
See also
Another demo of interactive item creation.
A more advanced example of status notifications.
// https://github.com/KubaO/stackoverflown/tree/master/questions/scene-polygon-7727656
#include <QtWidgets>
class MainWindow : public QWidget
{
Q_OBJECT
QGridLayout m_layout{this};
QPushButton m_new{"New"};
QPushButton m_erase{"Erase All"};
QLabel m_label;
QGraphicsView m_view;
public:
MainWindow() {
m_layout.addWidget(&m_new, 0, 0);
m_layout.addWidget(&m_erase, 0, 1);
m_layout.addWidget(&m_label, 0, 2);
m_layout.addWidget(&m_view, 1, 0, 1, 3);
m_view.setBackgroundBrush(Qt::black);
m_view.setAlignment(Qt::AlignBottom | Qt::AlignLeft);
m_view.scale(1, -1);
connect(&m_new, &QPushButton::clicked, this, &MainWindow::newItem);
connect(&m_erase, &QPushButton::clicked, this, &MainWindow::clearScene);
}
void setScene(QGraphicsScene * scene) {
m_view.setScene(scene);
}
Q_SIGNAL void newItem();
Q_SIGNAL void clearScene();
Q_SLOT void setText(const QString & text) { m_label.setText(text); }
};
class MyScene : public QGraphicsScene {
Q_OBJECT
public:
struct Status {
int paths;
int elements;
};
private:
bool m_newItem = {};
Status m_status = {0, 0};
QPainterPath m_path;
QGraphicsPathItem m_pathItem;
QGraphicsLineItem m_lineItem;
struct PathUpdater {
Q_DISABLE_COPY(PathUpdater)
MyScene & s;
PathUpdater(MyScene & scene) : s(scene) {
s.m_pathItem.setPath({}); // avoid a copy-on-write
}
~PathUpdater() {
s.m_pathItem.setPath(s.m_path);
s.m_status = {0, s.m_path.elementCount()};
for (auto i = 0; i < s.m_status.elements; ++i) {
auto element = s.m_path.elementAt(i);
if (element.type == QPainterPath::MoveToElement)
s.m_status.paths++;
}
emit s.statusChanged(s.m_status);
}
};
void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
PathUpdater updater(*this);
auto pos = event->scenePos();
m_lineItem.setLine(0, 0, pos.x(), pos.y());
m_lineItem.setVisible(true);
if (m_path.elementCount() == 0 || m_newItem)
m_path.moveTo(pos);
m_path.lineTo(pos.x()+1,pos.y()+1); // otherwise lineTo is a NOP
m_newItem = {};
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override {
PathUpdater updater(*this);
auto pos = event->scenePos();
m_lineItem.setLine(0, 0, pos.x(), pos.y());
m_path.setElementPositionAt(m_path.elementCount()-1, pos.x(), pos.y());
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override {
m_lineItem.setVisible(false);
}
public:
MyScene() {
addItem(&m_pathItem);
addItem(&m_lineItem);
m_pathItem.setPen({Qt::red});
m_pathItem.setBrush(Qt::NoBrush);
m_lineItem.setPen({Qt::white});
m_lineItem.setVisible(false);
}
Q_SLOT void clear() {
PathUpdater updater(*this);
m_path = {};
}
Q_SLOT void newItem() {
m_newItem = true;
}
Q_SIGNAL void statusChanged(const MyScene::Status &);
Status status() const { return m_status; }
};
int main(int argc, char *argv[])
{
using Q = QObject;
QApplication app{argc, argv};
MainWindow w;
MyScene scene;
w.setMinimumSize(600, 600);
w.setScene(&scene);
Q::connect(&w, &MainWindow::clearScene, &scene, &MyScene::clear);
Q::connect(&w, &MainWindow::newItem, &scene, &MyScene::newItem);
auto onStatus = [&](const MyScene::Status & s){
w.setText(QStringLiteral("Paths: %1 Elements: %2").arg(s.paths).arg(s.elements));
};
Q::connect(&scene, &MyScene::statusChanged, onStatus);
onStatus(scene.status());
w.show();
return app.exec();
}
#include "main.moc"

Printing QWidget (render) outside of GUI thread

I'm trying to render a QWidget on a QPrinter device without GUI blocking :
My print method looks like this :
void MyClass::print() {
QPrinter *printer = new QPrinter(QPrinter::HighResolution);
printer->setPageSize(QPrinter::A5);
printer->setPageOrientation(QPageLayout::Portrait);
printer->setColorMode(QPrinter::Color);
QPrintDialog *dialog = new QPrintDialog(printer);
if (dialog->exec() == QDialog::Accepted) {
MyWidget *_widget = new MyWidget( /* args */);
QPainter *painter = new QPainter;
painter->begin(printer);
double xscale = printer->pageRect().width() / double(_widget ->width());
double yscale = printer->pageRect().height() / double(_widget ->height());
double scale = qMin(xscale, yscale);
_widget ->setMinimumWidth((printer->pageRect().width() / scale));
_widget ->setMinimumHeight(printer->pageRect().height() / scale);
painter->translate(printer->paperRect().x() + printer->pageRect().width() / 2, printer->paperRect().y() + printer->pageRect().height() / 2);
painter->scale(scale, scale);
painter->translate(-_widget ->width() / 2, -_widget ->height() / 2);
_widget ->render(painter);
painter->end();
}
emit done();
}
With this function i have about 1-2 sec block state so i want to use QThread for this issue But Qt Doc says :
Although QObject is reentrant, the GUI classes, notably QWidget and
all its subclasses, are not reentrant. They can only be used from the
main thread. As noted earlier, QCoreApplication::exec() must also be
called from that thread.
And also :
In practice, the impossibility of using GUI classes in other threads
than the main thread can easily be worked around by putting
time-consuming operations in a separate worker thread and displaying
the results on screen in the main thread when the worker thread is
finished
I've modified Mandelbrot Example but there is nothing to show on screen in my case. my Widget should be rendered (time-consuming operation) and sent to printer that's all.
So do you have anything in mind for my situation ?
If the widget's paintEvent doesn't do much computation, then it'll be very fast to render the widget to a QPicture. A QPicture is just a record of all painter calls. You can then replay them on a printer in a concurrent job.
Alternatively, you can ensure that the widget is not used from the main thread (by staying invisible and not having a parent), and then it's ok to call render from any thread.

QWidget::grab function for videos: cannot catch

I want to save the current window to an image (like as ATL+PrintScreen) in Qt. Example, for the Qt Media Player Example (you can get all the code in Qt Creator examples when search "player", I added this code to player.h :
//private slots:
...
void exportVideoToImages();
and in player.cpp:
void Player::exportVideoToImages()
{
qDebug() << "ok";
QPixmap const& px = grab();
px.save("File.png");
}
Add this line to the the constructor to trigger the slot:
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_G), this, SLOT(exportVideoToImages()));
So, when I trigger CTRL+G, I will receive an image "File.png". It worked, but the problem is the playing video cannot be catch.
This is the two images, one from Alt+PrintScreen and one from the program:
Why is it? How can I grab the video in Qt? Can you show me?
Thank you very much!
I found the solution:
QScreen *screen = QGuiApplication::primaryScreen();
if (screen) {
QPixmap px =screen->grabWindow(this->winId());
px.save("screen.png");
}

Sleep inside a loop that uses paintevent in qt c++

Basically what I wanna do is to draw rectangles for each number in my list. The bigger the number is, the larger the rectangle is.
My problem is when I actually wanna do it, step-by-step, and waiting a few seconds between every drawing. I've looked out for a few solutions but I can't get them to work for this particular case. I saw I could use fflush to release whatever it's in the buffer but I don't know how I can use it for this.
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(QBrush(Qt::green, Qt::SolidPattern));
int weight=300/lista.size;
int posx=weight;
for (int i=1; i<=lista.size; i++){
List_node * node = list.get_element_at(i);
int num=node->getValue(); //this returns the value of the node
if (i==3){
painter.setBrush(QBrush(Qt::red, Qt::SolidPattern)); // this line is to draw a rectangle with a different color. Testing purposes.
}
painter.drawRect(posx,400-(num*10),weight,num*10);
sleep(1); //this sleep isn't working correctly.
painter.setBrush(QBrush(Qt::green, Qt::SolidPattern));
posx+=weight;
}
Any help would be really appreciated.
sleep() won't work for this -- it blocks the Qt event loop and keeps Qt from doing its job while it is sleeping.
What you need to do is keep one or more member variables to remember the current state of the image you want to draw, and implement paintEvent() to draw that current single image only. paintEvent() (like every function running in Qt's GUI thread) should always return immediately, and never sleep or block.
Then, to implement the animation part of things, set up a QTimer object to call a slot for you at regular intervals (e.g. once every 1000mS, or however often you like). Implement that slot to adjust your member variables to their next state in the animation-sequence (e.g. rectangle_size++ or whatever) and then call update() on your widget. update() will tell Qt to call paintEvent() again on your widget as soon as possible, so your display will be updated to the next frame very shortly after your slot method returns.
Below is a trivial example of the technique; when run it shows a red rectangle getting larger and smaller:
// begin demo.h
#include <QWidget>
#include <QTimer>
class DemoObj : public QWidget
{
Q_OBJECT
public:
DemoObj();
virtual void paintEvent(QPaintEvent * e);
public slots:
void AdvanceState();
private:
QTimer _timer;
int _rectSize;
int _growthDirection;
};
// begin demo.cpp
#include <QApplication>
#include <QPainter>
#include "demo.h"
DemoObj :: DemoObj() : _rectSize(10), _growthDirection(1)
{
connect(&_timer, SIGNAL(timeout()), this, SLOT(AdvanceState()));
_timer.start(100); // 100 milliseconds delay per frame. You might want to put 2000 here instead
}
void DemoObj :: paintEvent(QPaintEvent * e)
{
QPainter p(this);
p.fillRect(rect(), Qt::white);
QRect r((width()/2)-_rectSize, (height()/2)-_rectSize, (_rectSize*2), (_rectSize*2));
p.fillRect(r, Qt::red);
}
void DemoObj :: AdvanceState()
{
_rectSize += _growthDirection;
if (_rectSize > 50) _growthDirection = -1;
if (_rectSize < 10) _growthDirection = 1;
update();
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
DemoObj obj;
obj.resize(150, 150);
obj.show();
return app.exec();
}