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.
Related
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.
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.
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));
}
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())
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.