Previous image effect while switching - qt

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.

Related

gif image in QLabel

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);

How to set animated icon to QPushButton in Qt5?

QPushButton can have icon, but I need to set animated icon to it. How to do this?
I created new class implemented from QPushButton but how to replace icon from QIcon to QMovie?
This can be accomplished without subclassing QPushButton by simply using the signal / slot mechanism of Qt. Connect the frameChanged signal of QMovie to a custom slot in the class that contains this QPushButton. This function will apply the current frame of the QMovie as the icon of the QPushButton. It should look something like this:
// member function that catches the frameChanged signal of the QMovie
void MyWidget::setButtonIcon(int frame)
{
myPushButton->setIcon(QIcon(myMovie->currentPixmap()));
}
And when allocating your QMovie and QPushButton members ...
myPushButton = new QPushButton();
myMovie = new QMovie("someAnimation.gif");
connect(myMovie,SIGNAL(frameChanged(int)),this,SLOT(setButtonIcon(int)));
// if movie doesn't loop forever, force it to.
if (myMovie->loopCount() != -1)
connect(myMovie,SIGNAL(finished()),myMovie,SLOT(start()));
myMovie->start();
Since I had to solve this problem for a project of mine today, I just wanted to drop the solution I found for future people, because this question has lots of views and I considered the solution quite elegant. The solution was posted here. It sets the icon of the pushButton every time, the frame of the QMovie changes:
auto movie = new QMovie(this);
movie->setFileName(":/sample.gif");
connect(movie, &QMovie::frameChanged, [=]{
pushButton->setIcon(movie->currentPixmap());
});
movie->start();
This also has the advantage, that the icon will not appear, until the QMovie was started. Here is also the python solution, I derived for my project:
#'hide' the icon on the pushButton
pushButton.setIcon(QIcon())
animated_spinner = QtGui.QMovie(":/icons/images/loader.gif")
animated_spinner.frameChanged.connect(updateSpinnerAniamation)
def updateSpinnerAniamation(self):
#'hide' the text of the button
pushButton.setText("")
pushButton.setIcon(QtGui.QIcon(animated_spinner.currentPixmap()))
Once you want to show the spinner, just start the QMovie:
animated_spinner.start()
If the spinner should disappear again, then stop the animation and 'hide' the spinner again. Once the animation is stopped, the frameChanged slot won't update the button anymore.
animated_spinner.stop()
pushButton.setIcon(QtGui.QIcon())

Qt QLabel fails to resize

I implemented QLabel much like Qt's ImageViewer example, except I use QGridLayout for positioning my widgets. I also implemented similar lines for scaling my QLabel using QScrollBar, but QLabel just doesn't scale like it should inside the QScrollArea. I am not sure if it is related to some kind of GridLayout management issue or something else. I have been reading everywhere and trying different things for 3 days now. Below I list the relevant portion of my code.
In my Viewer class constructor:
{
imageLabel1 = new QLabel;
imageLabel1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
imageLabel1->setScaledContents(true);
scrollArea1 = new QScrollArea;
scrollArea1->setWidget(imageLabel1);
scrollArea1->setWidgetResizable(true);
....
QGridLayout *centralLayout = new QGridLayout;
centralLayout->addWidget(scrollArea1, 0, 0);
...}
and my scaleImage method:
void Viewer::scaleImage1(int factor)
{
Q_ASSERT(imageLabel1->pixmap());
scaleFactor *= (1 + factor);
//imageLabel1->resize(scaleFactor* imageLabel1->pixmap()->size());
imageLabel1->pixmap()->toImage().scaled(scaleFactor* imageLabel1->pixmap()->size(), Qt::KeepAspectRatio, Qt::FastTransformation);
imageLabel1->adjustSize();
adjustScrollBar(scrollArea1->horizontalScrollBar(), factor);
adjustScrollBar(scrollArea1->verticalScrollBar(), factor);
imageLabel1->update();
}
My scaleImage1 function is a public slot, and it receives signal from a scrollbar that goes between 0 and 2 so that, into the scaleFactor, the imageLabel1 is designed to be capable of being zoomed in up to 3 times its original size. But when I run the code, I don’t observe the imageLabel becoming enlarged inside the QScrollArea, which I saw in the imageViewer demo. The imageLabel1 simply retains the original size as it is loaded and does not respond to the valueChange() of scrollbar.
I'd appreciate your advice/tips very much.
I think is because you set QSizePolicy::Minimum to the imageLabel, try with MinimumExpanding or other that better fit your needs.

Moving a QPixmap to the corner of a QGraphicsScene

I have a QGraphicsView and a QGraphicsScene connected like this:
graphicsScene->setSceneRect(this->graphicsView->rect());
graphicsView->setScene(this->Scene);
Then I load an image and add it to the scene:
QPixmap pixmap;
pixmap.load(fileName);
pixmap = pixmap.scaled(this->graphicsView->size());
QGraphicsPixmapItem* item = this->Scene->addPixmap(pixmap);
Now, as described in the documentation, the image corner is at (0,0), which is not the corner of the graphicsScene. I know I can position the resulting pixmap by doing:
item->setPos(this->Scene->sceneRect().x(), this->Scene->sceneRect().y());
However, I can't seem to make sense of the coordinates of the rect's of the scene or the view. Can anyone explain how I would move the pixmap to the corner of the scene/view?
Thanks,
David
EDIT: Here is the full form constructor. The QGraphicsView was created in Qt Designer and is inside of a GridLayout:
Form::Form(QWidget *parent)
: QWidget(parent)
{
setupUi(this);
QGraphicsScene* scene = new QGraphicsScene;
scene->setSceneRect(this->graphicsView->rect());
this->graphicsView->setScene(scene);
QPixmap pixmap;
pixmap.load("image.png");
pixmap = pixmap.scaled(this->graphicsView->size());
scene->addPixmap(pixmap);
}
I also tried this:
QGraphicsScene* scene = new QGraphicsScene;
this->graphicsView->setScene(scene);
QPixmap pixmap;
pixmap.load("/home/doriad/glasses.jpg");
QGraphicsPixmapItem * item = scene->addPixmap(pixmap);
this->graphicsView->fitInView (item);
but the image appears tiny, rather than filling up the view like I would expect. Can anyone explain this?
The full project and image are available here: daviddoria.com/Uploads/qt/QPixmapPosition
Don't worry about scaling the pixmap yourself or even translating it, let the view do it for you.
Use graphicsView->fitInView(pixmap); but you should read the documentation for :
Qt's Graphics View Framework
void QGraphicsView::setSceneRect (QRectF )
void QGraphicsView::translate ( qreal dx, qreal dy )
void QGraphicsView::fitInView ( const QGraphicsItem * item, ... )
The way that QGraphicsScene and QGraphicsView interact is that you can have a single scene with at least one or more views.
A good example I like to think of is a zoomed in view of part of a map with a mini view of the entire map in the corner. There are two views, one of part of the map and one of the entire map, with one scene, the map itself.
So you put items in your scene and all the items in the scene are drawn relative in size to each other. The "scene rect" of your view, by default, scales to fit the items in the view until one unit in the scene is one pixel in the view or until it needs to zoom out to fit all the items in your scene.
If you call fitInView(someItem) it should scale your view of the scene so that the item specified fills it up and translates the view so that it is centered. If you need to translate or scale it more use the translate or scale functions in QGraphicsView.
When you are jumping between coordinate systems of your scene and view with your QRect's or QPoint's, use the helper functions: mapToScene and mapFromScene from QGraphicsView.
Try this:
QGraphicsScene* scene = new QGraphicsScene;
scene->setSceneRect(graphicsView->sceneRect());
QPixmap pixmap;
pixmap.load("/home/doriad/glasses.jpg");
pixmap=pixmap.scaledToWidth(this->graphicsView->width());
QGraphicsPixmapItem * item = scene->addPixmap(pixmap);
graphicsView->setScene(scene);
By default, the pixmap will be at (0,0) in the scene, and the scene will be at (0,0) in the view. QWidgets are sized by pixels. If you had a QGraphicsView the size of the screen and the resolution is 1440 x 900, you can position objects in that view from (0,0) or the top left corner of the screen, to (1440,900) the bottom right corner of the screen. Most QGraphicsItems are placed with reference to their top left corner. So placing a pixmap at (0,0) aligns the top left corner of the pixmap with the top left corner of the scene it's placed in. If your pixmap 'hangs off the bottom' of your view, try using:
pixmap=pixmap.scaledToHeight(this->graphicsView->height());
If you use the function:
this->graphicsView->fitInView (item);
The graphicsView will only scroll to the point where your item fits in the view.
I downloaded David's code and ran it two ways. Click the links to see the results.
1) with pixmap=pixmap.scaledToHeight(this->graphicsView->height());
2) with pixmap=pixmap.scaledToHeight(200);
I don't know enough to explain why this is happening, but I thought it would be a useful data point.
It turns out the problem was that the GraphicsView was in a layout. In my example, the resizing of the image was done in the Form constructor. Apparently this is before the layout takes its shape? I moved the code to a pushButton and when I click it the image is sized how I would expect.
I got the image to stay sized to the GraphicsView in the Layout by subclassing QGraphicsView and reimplementing :
class CustomGraphicsView: public QGraphicsView
{
Q_OBJECT
{
void resizeEvent ( QResizeEvent * event )
{
emit resized();
}
signals:
void resized();
}
Then I connect this resized() signal to a slot that simply calls this->View->fitInView (this->ImageToTraceItem);
David

How to Add QListView/QListWidget to QGraphicsScene in Qt?

How to Add QListView/QListWidget to QGraphicsScene and add Widgets to ListView
When i Try to add QLisView to QGraphicsScene mouse scroll affects goes from Scene.
I want to add QPushButtons as ListView Items in QgraphicsScene with mouse scroll affect.
Thanks.
What about QGraphicsProxyWidget?
QListView *listView = new QListView;
QGraphicsProxyWidget *proxy = scene.addWidget(listView);
Then (or before that) you can populate the list with anything you want. QPushButton can be added to the list using setIndexWidget(). Also you might rethink the whole idea of having a QListView, and give it a try with QScrollArea and a linear layout containing buttons. That would require a bit more logic to organize items within the scroll area, but it should be more lightweight that QListView with custom widgets.
I second the answer above: ProxyWidget is the answer.
Here is my working code,
Header:
class CenterScreen{
private:
QListWidget* nameListWidget;
QGraphicsProxyWidget* nameProxyWidget;
...
C++ source:
void CenterScreen::addListView()
{
QGraphicsScene* scene = ui.centerGraphicsView->scene();
nameListWidget = new QListWidget();
nameProxyWidget = scene->addWidget(nameListWidget);
...
nameProxyWidget->hide(); // you can control your widget as you like

Resources