Im trying to make an application that takes live image recorded by camera and shows it on screen using Qt GUI. The camera driver and api provides me with functionality that refreshes the memory block showed by pointer.
The problem is my image won't refresh inside Qlabel made with Qimage (code below)
QImage myImage(raw_image_data_pointer, 768, 576, QImage::Format_RGB32 );
QLabel myLabel;
myLabel.setPixmap(QPixmap::fromImage(myImage));
myLabel.show();
How can i get my QLabel to be refreshed?
The problem is that you create the label widget with a static image inside. The containing image is not "connected" to your camera anymore, but is just a copy of a frame of your video stream. In order to make QLabel to update itself, you have to constantly replace the containing image with new one. For example, you can set up a timer for that:
MyClass:MyClass()
{
QTimer *timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(10); // Defines how often update the label.
myLabel = new QLabel;
[..]
}
// Slot
MyClass::update()
{
QImage myImage(raw_image_data_pointer, 768, 576, QImage::Format_RGB32 );
myLabel.setPixmap(QPixmap::fromImage(myImage));
}
Related
I have a problem, i'm developing a graphical program under Windows, there are few QGraphicsScene and one QGraphicsView that it is possible to change the Scenes in runtime with a lot of graphics items, the problem is when I use Qwidget viewport everything works but when I switch to OpenGL viewport when I change the scene the content of previous scene still appear on the QGraphicsView and the contents of new Scene appear too.
what is the problem ? is it the changing Scenes method best solution or should I change the method ?
here is the code to setup View
m_viewPort = new QOpenGLWidget (this);
QSurfaceFormat format;
format.setProfile(QSurfaceFormat::CoreProfile);
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setSamples(4);
m_viewPort->setFormat(format);
ui->gV->setViewport(m_viewPort);
ui->gV->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
//ui->gV->setCacheMode(QGraphicsView::CacheBackground);
ui->gV->setRenderHints(QPainter::Antialiasing| QPainter::HighQualityAntialiasing | QPainter::SmoothPixmapTransform| QPainter::TextAntialiasing);
ui->gV->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->gV->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
ui->gV->setTransformationAnchor(QGraphicsView::NoAnchor);
ui->gV->setAutoFillBackground(false);
ui->gV->setAttribute(Qt::WA_OpaquePaintEvent, true);
ui->gV->setAttribute(Qt::WA_NoSystemBackground, true);
resize(boardBaseSize);
here is code to set the new scene to the view
void GlScreenBoard::setShowScene(QGraphicsScene *scene, QString programName)
{
scene->setSceneRect(boardSceneRectBase);
ui->gV->setScene(scene);
}
another problem is when I set Graphics view CacheMode to CacheBackground the OpenGL viewport disables !! and the painter in QGraphicsScene returns to Raster !
I'm writing an image viewer which allows me to do some actions. As a visual feedback to some actions (like copy/move/delete/..) I'd like to have a decent popup in the middle of my application window which informs about what has been done and which disappears after about a second.
Of course I can just use a Widget and and modify it to fit my needs:
placed in the middle/on top of application window (regardless of layout)
disappears after a given time
no interaction/focus possible - clicking on the notification should be like clicking on what's behind of it
decent style (e.g. transparent and easily readable)
.. I'm just wondering if there's something dedicated for this purpose
(I'm NOT talking about tray notifications which appear near to some task barof the window manager)
You can achieve a nice popup fade in/fade out effect using animation effects in qt ,sample code is given below :
QGraphicsOpacityEffect* effect=new QGraphicsOpacityEffect();
this->label->setGraphicsEffect(effect);
this->label->setStyleSheet("border: 3px solid gray;border-radius:20px;background-color:#ffffff;color:gray");
this->label->setAlignment(Qt::AlignCenter);
this->label->setText("Your Notification");
QPropertyAnimation* a=new QPropertyAnimation(effect,"opacity");
a->setDuration(1000); // in miliseconds
a->setStartValue(0);
a->setEndValue(1);
a->setEasingCurve(QEasingCurve::InBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
this->label->show();
connect(this->timer,&QTimer::timeout,this,&Notifier::fadeOut);
this->timer->start(2000); // 1000 ms to make the notification opacity full and 1000 seconds to call the fade out so total of 2000ms.
and your fadeout method as:
void fadeOut(){
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
this->label->setGraphicsEffect(effect);
QPropertyAnimation *a = new QPropertyAnimation(effect,"opacity");
a->setDuration(1000); // it will took 1000ms to face out
a->setStartValue(1);
a->setEndValue(0);
a->setEasingCurve(QEasingCurve::OutBack);
a->start(QPropertyAnimation::DeleteWhenStopped);
connect(a,SIGNAL(finished()),this->label,SLOT(hide()));
}
It sound like you want to use a QMessageBox. For instance:
QMessageBox* msgbox = new QMessageBox(this);
msgbox->setWindowTitle("Note");
msgbox->setText("Successfully copied item foobar");
msgbox->open();
You might want to change the modality according to your desire and implement a timer to close the dialog.
QTimer* timer = new QTimer(this);
QObject::connect(timer, SIGNAL(timeout()), msgbox, SLOT(close()));
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(stop()));
QObject::connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
timer->start(1000);
Note: Example code, not tested.
Dont know if you are in python or C, but may have a look to this:
http://doc.qt.io/qt-4.8/qmessagebox.html
Nevertheless, I would go for a new window (QWidget) and modify it. Its only a couple of lines, and the automatic close you can do by a Qtimer.
I am using QGraphicsScene to display video. I have two modes in which two different videos are shown. I am using a QGraphicsPixmapItem and displaying the video on it. When I switch from one video to other it does not show the latest frame immediately. For 2 or 3 seconds the last seen image for that view is visible. I have tried using removeItem() and delete for removing the pixmap items every time I switch the views but it is not helping. Can some one suggest me how to solve this?
//Member variables
QGraphicsPixmapItem *m_pixItemZoomTrue;
QGraphicsPixmapItem *m_pixItemZoomFalse;
QGraphicsScene m_sceneZoomTrue;
QGraphicsScene m_sceneZoomFalse;
//Slot called on button click
void ZoomSelectionTrue()
{
//Remove the item from scene of ZoomSelectionFalse
m_sceneZoomFalse->removeItem(m_pixItemZoomFalse);
//Add item to the scene of ZoomSelectionTrue
m_pixItemZoomTrue = new QGraphicsPixItem();
m_sceneZoomTrue->addItem(m_pixItemZoomTrue);
//Rest code for adding pixmap to the pix item.
}
void ZoomSelectionFalse()
{
m_sceneZoomTrue->removeItem(m_pixItemZoomTrue);
m_pixItemZoomFalse = new QGraphicsPixItem();
m_sceneZoomFalse->addItem(m_pixItemZoomFalse);
//Rest code for adding pixmap to the pix item.
}
Thank You.
I want to add a gif animated image in QLabel that add into the QGraphicsScene.
My code is here:
QLabel *lbl = new QLabel;
QMovie *mv = new QMovie(":/Images/sun.gif");
mv->start();
lbl->setWindowFlags(Qt::FramelessWindowHint);
lbl->setMask((new QPixmap(":/Images/sun.gif"))->mask()); // for create transparent for QLabel image
lbl->setMovie(mv);
lbl->setGeometry(10,10,10,10);
scene.addWidget(lbl);
but when I run that it will transparent with first frame of that gif and when the gif is running the photo will not show completely and it will run with transparented area in the first frame.
How can I solve that?
Thanks
The problem is that QLabel has window background by default. You're trying to remove it by do it incorrectly:
FramelessWindowHint doesn't make sense here, since it's only used for top level widgets, and a widget added to scene is technically hidden and doesn't have system window frame. This line should be removed.
setMask does exactly what you describe it does. Since QPixmap is not animated, its mask is the alpha mask of the first frame of animation. And you permanently apply this mask to the label. It's not surpising that it works, but obviously it's not what you want. This line should also be removed.
setGeometry line is incorrect. It prevents picture from being visible for me. Label has good size by default and there is no need for setGeometry. If you want to scale or move the item on the scene, you can do it after addWidget as for any other QGraphicsItem. E.g. addWidget(lbl)->setPos(10, 10).
The magic bullet you need is WA_NoSystemBackground. It disables background painting for QLabel completely. So, the full code would be:
QLabel *lbl = new QLabel;
QMovie *mv = new QMovie("c:/tmp/sun.gif");
mv->start();
lbl->setAttribute(Qt::WA_NoSystemBackground);
lbl->setMovie(mv);
scene.addWidget(lbl);
It works fine for me. However I consider it over-complicated. You should not use proxy widgets in scene unless necessary. You can easily add a movie using QMovie and QGraphicsPixmapItem and switching pixmaps as movie frames change. I wrote a convenient class for this:
Header:
class GraphicsMovieItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
GraphicsMovieItem(QGraphicsItem* parent = 0);
void setMovie(QMovie* movie);
private:
QMovie* m_movie;
private slots:
void frameChanged();
};
Source:
GraphicsMovieItem::GraphicsMovieItem(QGraphicsItem *parent)
: QGraphicsPixmapItem(parent), m_movie(0) {
}
void GraphicsMovieItem::setMovie(QMovie *movie) {
if (m_movie) {
disconnect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frameChanged()));
}
m_movie = movie;
if (m_movie) {
connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frameChanged()));
}
frameChanged();
}
void GraphicsMovieItem::frameChanged() {
if (!m_movie) { return; }
setPixmap(m_movie->currentPixmap());
}
Usage:
QMovie *mv = new QMovie("c:/tmp/sun.gif");
GraphicsMovieItem* item = new GraphicsMovieItem();
item->setMovie(mv);
scene.addItem(item);
I want to draw an image, pixel by pixel at run time. I use QPainter and paintEvent to draw. But when paintEvent is called each time, the previously drawn image is cleared and the new point has been drawn.
How to avoid clearing the previously drawn parts? I just want to append the new pixel point to the previously drawn points.
Lines::Lines(QWidget *parent)
: QWidget(parent)
{
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_timer->start();
m_x = 0;
m_y = 0;
}
void Lines::paintEvent(QPaintEvent *event)
{
QPen pen(Qt::black, 2, Qt::SolidLine);
QPainter painter(this);
painter.setPen(pen);
painter.drawPoint(m_x, m_y);
}
void Lines::updateStatus()
{
m_x++;
m_y++;
update();
}
paintEvent is supposed to do a complete redraw of the widget region specified in the event.
So you are responsible for buffering previous results.
It doesn't really make sense to change the desired output in paintEvent, as it may be randomly called and when it is called is out of your control.
If you want to avoid that you can use a QGraphicsView.
Buffering could be done using a QPixmap, which would be part of the Lines class. You draw the pixel in the pixmap (not in the paint event, in updateStatus), and draw the pixmap in the paint event.
QWidget::setAttribute( WA_OpaquePaintEvent, true );
prevents clearing the widget. However, this is just for optimization in case the widget does a complete repaint anyway.
You should follow Dr. Hirsch's advice.