How to set animated icon to QPushButton in Qt5? - qt

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

Related

Qt this->update() is not entirely updating the widget

I created a simple widget with a button, a slot for the button, a resize event and a paint event.
I expect when I click on the button it draws an ellipse at a random position and the button disappears.
But I get: the ellipse is drawn and the button is not hidden after this->update.
Even stranger, when I uncomment the button->hide(); every time I click it draws a new eclipse but the old ellipses are still there. Something is wrong with updating and the paint event.
If I resize the window by dragging with the mouse the update of the paint event works as expected. Only the last ellipse stays and the button is hidden.
My Qt version is Qt_5_15_2_MinGW_32_bit
Here is the code of the widget:
PATrackSetter::PATrackSetter(QWidget *parent) : QWidget(parent){
button = new PAButton(this);
connect(button,SIGNAL(clicked(int, QString, QString)),this,SLOT(on_TileClicked(int, QString, QString)));
button->setFixedSize(100, 100);
button->move(0,0);
button->show();
}
void PATrackSetter::paintEvent(QPaintEvent *){
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QPen pen = QPen();
pen.setColor(Qt::yellow);
painter.setPen(pen);
painter.drawEllipse(100,rand() % 500 +10,5,5);
}
void PATrackSetter::resizeEvent(QResizeEvent *)
{
}
void PATrackSetter::on_TileClicked(int buttonID, QString buttonText, QString newButtonStatus){
button->hide();
this->update();
}
Can anyone see what I did wrong?
Edit:
I added more code to the project and I run into the same issue. I added the following lines into the MainWindow class and the updating inside the PATrackSetter widget doesn't work anymore as expected. I really dont understand why. But if I uncomment these lines it works again well.
QPalette paletteBGColor;
QBrush brush;
brush.setColor(Qt::black);
paletteBGColor.setBrush(QPalette::Background, brush);
this->setPalette(paletteBGColor);
Case closed.
If the button is not hidden then slot is not called. I guess you didn't put void on_TileClicked(int, QString, QString) in slots: section in header file, or signal/slot signatures don't match (in which case there must be warning in debug output in runtime).
When you are reimplementing paintEvent you should expect that every update on the QWidget, even manually or by the parent window, will call the paintEvent once. So, it's up to you to handle cleaning the previous state or draw on the previous drawings. The behavior you explained is quite normal.
It seems that you are not calling setGeometry on the PATrackSetter when you are instantiating it. So, in the update hierarchy, its size is not known and you should expect partial redraws and undefined behaviors.

Qt: Map a clicked signal to another button

I wanted to have a collapsible widget. I used this code: How to make an expandable/collapsable section widget in QT.
I wanted the title of the QToolButton to be on the far left and the triangle icon to be on the right. I deleted the title, and moved the icon. Then I created a QPushButton and made it look like a QLabel and positioned it where I wanted the title to be. Now, I would like the title to be clickable - to have the same effect as clicking on the toggle icon would have. How do I connect these two signals?
Code for the QToolButton:
QObject::connect(toggleButton, &QToolButton::clicked, [this](const bool checked)
{
toggleButton->setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::UpArrow);
toggleAnimation->setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
toggleAnimation->start();
});
You can also write it like this:
QObject::connect(titleLabel, &QPushButten::clicked, toggleButton, &QToolButton::clicked);
I found that this works:
QObject::connect(titleLabel, &QPushButton::clicked, [this]
{
toggleButton->click();
});
Disclaimer: I have no idea if that is the correct way to do this.

Qt5: show notification popups

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.

Changing fluently brightness of QPushButton icon

I found a function to make QImage brighter and used in my Qt application.
I want to show simple "animation" of making button step by step brighter and than again step by step back to initial state after user click it.
Here my code:
void Widget::on_stopButton_clicked(){
player.stop();
for(int i = 0; i <= 50; ++i){
QImage* image = new QImage(":/Graphics/Graphics/StopButton.png");
changeBrightness(*image, i);
QPixmap* pixmap = new QPixmap(QPixmap::fromImage(*image));
ui->stopButton->setIcon(QIcon(*pixmap));
QThread::msleep(50);
}
}
It doesn't work as I expected...
I see only the final effect, so the last call:
changeBrightness(*image, 50);
It seems that user can see changes on form only after function ends, is it right?
Is there other way to make such "animation"?
You do not give Qt any time to redraw the widget after you update the button's image, because you are stuck in the loop. Only after you finished updating the image, Qt will be able to redraw your widget, which is why you only see the final result.
Look into QTimer. You can set its timeout to 50 milliseconds via QTimer::setInterval. Then connect a slot that changes the color of the button's image to QTimer::timeout. This slot will be much like your code, but without the loop. E.g. each call of the slot is a single iteration of your loop. Finally, to start or stop the animation, you call QTimer::start or QTimer::stop.

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