I can't seem to find any info about this. but a lot of kde apps use animated icons.
As i know it setting as QIcon a gif won't work, as only first frame will be displayed.
I didn't try this but it is probably possible by setting new icon every few milliseconds.
/* list of frames */
QLinkedList<QIcon> frames;
/* frames are icons created from images in application resources */
frames << QIcon(":/images/icon1.png") << QIcon(":/images/icon2.png");
/* set timer */
QTimer timer = new QTimer(this);
timer->setSingleShot(false);
connect(timer, SIGNAL(timeout()), this, SLOT(updateTrayIcon()));
timer->start(500); /* update icon every 500 milliseconds */
/*
updateTrayIcon function (SLOT) sets next tray icon
(i.e. iterates through QLinkedList frames)
*/
I suppose that you have two ways:
Try to use GIF animated file (start playing with GIF with QMovie), and place it to the tray (i'm not sure about this case)
The other way is to use QTimer and a few different images. Here I found an example.
I did like this:
QMovie *movie = new QMovie(":/icons/icon.gif");
QLabel *label = new QLabel(this);
label->setMovie(movie);
movie->start();
QTimer *timer = new QTimer(this);
timer->setSingleShot(false);
connect(timer, &QTimer::timeout, [this,timer,label](){
trIcon->setIcon(label->movie()->currentPixmap());
timer->start(50);
});
timer->start(50);
Related
I want to display a video when a button is clicked and close the video widget once the video is done playing in QT 6.4, and if there is no workaround that, if I could somehow display the video in a widget I create on QTDesigner that would work well too, thank you!
QMediaPlayer* player = new QMediaPlayer;
player->setSource(QUrl("C:/Users/QTDev/Desktop/final.mp4"));
QAudioOutput * audioOutput = new QAudioOutput;
player->setAudioOutput(audioOutput);
audioOutput->setVolume(1.0);
QVideoWidget* videoWidget = new QVideoWidget;
player->setVideoOutput(videoWidget);
videoWidget->showFullScreen();
player->play();
I'm trying to build a simple memory game with Qt 5.11.1 and C++, where you get a few tiles on screen and you have to click on two and try to match the images they show.
My tiles are implemented as QPushButtons. Each time you click on one an image is displayed (by calling a showImage()method that changes the button background). When a second tile is clicked, if there is a match the two buttons are disabled so you can't click on them again (and you get a higher score). However, if you didn't get a match, the two tiles you just clicked will go back to their initial state (showing no image) after 1 second (this allows the user to "memorize" which image was showing up on each tile).
Whenever you click on a "tile" (button) it becomes disabled (button->setEnabled(false)). If after clicking a second tile there was no match, then both tiles are turned back and then setEnabled(true) again. I'm using a single shot QTimer to call the method that will turn back the tiles:
QTimer::singleShot(1000, this, SLOT(turnTilesBack()));
firstTile->setEnabled(true);
secondTile->setEnabled(true);
Everything is working as expected, except for one thing: as QTimer runs in its own thread (or so I understand from what I read) all of the available tiles remain enabled during the 1000 milisecond lapse, allowing the user to continue clicking on them. However, when there is no match, I'd like to "freeze" the buttons until the QTimer has timed out so the user can't continue playing until the tiles have turned back.
So instead of using the QTimer I've trying this solution which I saw on this question (How do I create a pause/wait function using Qt?):
QTime dieTime= QTime::currentTime().addSecs(1);
while (QTime::currentTime() < dieTime)
turnTilesBack();
although I removed this line: QCoreApplication::processEvents(QEventLoop::AllEvents, 100); as this would cause the main thread not to freeze and buttons would still be clickable.
But with this approach, whenever the user clicks on the second tile, if there is no match the image is not even displayed, even when my showImage() method is called before the code above, and I'm not sure why this is. So the user knows there was no match because after 1 second the tiles go back to their initial state, but they never got to see the image on the second button.
As another approach, I also though of disabling all buttons and then after the single shot QTimer times out, re-enabling back only the ones that have not been matched yet. But this would require additional logic to keep track of which tiles have been matched. So for now I'm sticking to the
Is there a cleaner solution? Maybe there's a way to make the QTimer freeze the main thread until it times out?
An easy way to enable/disable the entire group of QPushButtons is to place them on an intermediate widget (in the below example I've used a QFrame)
If you want to disable all the QPushButtons, you just disable the frame, and all its child widgets will be disabled.
When you want to re-enable them, you enable the frame.
Any widgets inside the frame which are already disabled won't be enabled when the frame is re-enabled, so you won't lose your enabled/disabled state on the individual buttons
Here is a simple example. Note that I've used explicit enable/disable buttons which act as a proxy for your timer.
#include <QApplication>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QFrame>
int main(int argc, char** argv)
{
QApplication* app = new QApplication(argc, argv);
QMainWindow* window = new QMainWindow();
window->setFixedSize(1024, 200);
QWidget* widget = new QWidget();
QHBoxLayout layout(widget);
QPushButton* enable = new QPushButton("enable");
QPushButton* disable = new QPushButton("disable");
QFrame* frame = new QFrame();
layout.addWidget(enable);
layout.addWidget(disable);
layout.addWidget(frame);
QVBoxLayout frame_layout(frame);
for (int i = 0; i < 5; ++i)
frame_layout.addWidget(new QPushButton("click"));
// this shows that an already disabled button remains disabled
QPushButton* already_chosen = new QPushButton("click");
frame_layout.addWidget(already_chosen);
already_chosen->setEnabled(false);
QObject::connect(enable, &QPushButton::clicked, [&]{ frame->setEnabled(true); });
QObject::connect(disable, &QPushButton::clicked, [&]{ frame->setEnabled(false); });
window->setCentralWidget(widget);
window->show();
return app->exec();
}
I have a QWidget which I want to use like a Dialog on top of another QWidget.
What I'm trying to do is a simple "Please wait while yadda yadda..." dialog with no buttons.
The code section is as follows:
void NewWindow::on_combobox_New_currentIndexChanged(int index) //slot function
{
QWidget* box = new QWidget();
box->setWindowModality(Qt::ApplicationModal);
box->setWindowTitle("Wait...");
QHBoxLayout* layout = new QHBoxLayout();
box->setLayout(layout);
QLabel* lbl = new QLabel();
lbl->setText("Loading...");
layout->addWidget(lbl);
box->show();
for (int var = 0; var < st.size(); ++var)
{
//Some heavy lifting here
}
box->close();
}
Normally I would expect this dialogue box to appear with the proper text and disappear after the loop ends. In fact it does that too but with one difference: The label does not display. The widget looks empty. Then disappears.
If I copy the code into a different area (for example to the MainWindow constructor) it displays properly with the message in it.
I sense that the loop blocks the draw operation but then the widget itself should be missing too. Why it is only the label eludes me.
Thanks for any insight.
Since you are creating and displaying this widget in a QObject slot, and then before returning from the slot, closing the widget, by the time Qt goes through the process of executing all your instructions, the last one is close, and so the widget disappears from view.
Underneath your slot, Qt is running in an event loop. Since control is never returned to the event loop, Qt never has an opportunity to render the graphics you've requested of it.
When you create widgets, add labels, etc, you are actually registering a bunch of commands with the event loop, which will only later be processed.
If you want Qt to render any changes you have made whilst in a slot, before returning to the event loop, you have to call processEvents.
Without doing so, you won't see those changes until control passes back to the Qt event loop.
So what is happening here, is that since you're also closing the widget at the end of your slot, Qt will create the widget, render its contents, and then immediately close it, and you won't see anything.
The reason for this is so that Qt can do calculations on what is visible, what isn't, be smart about what it renders etc, and only decide to draw what is necessary.
If it just rendered everything immediately, without waiting for control to return to it so it can process the "next batch of updates", it would likely be horribly inefficient.
So you need to put processEvents inside your callback slot.
void NewWindow::on_combobox_New_currentIndexChanged(int index) //slot function
{
QWidget* box = new QWidget();
box->setWindowModality(Qt::ApplicationModal);
box->setWindowTitle("Wait...");
QHBoxLayout* layout = new QHBoxLayout();
box->setLayout(layout);
QLabel* lbl = new QLabel();
lbl->setText("Loading...");
layout->addWidget(lbl);
box->show();
QCoreApplication::processEvents(); // cause the box to be displayed
for (int var = 0; var < st.size(); ++var)
{
//Some heavy lifting here
// if you do anything here to change the widget, such as
// updating a progress bar, you need to `processEvents` again
QCoreApplication::processEvents();
}
box->close();
}
As for why the widget window appears, but not the contents, this is likely because when creating the widget Qt sends a message to the Window Manager (in this case MS Windows), which will create the window on which Qt can render its contents.
So you see the result of Windows creating a new window for Qt, but nothing from Qt painting on that window itself.
I am trying to use one blinking image in QGraphicsPixmapItem. The image has shown without the animation effect. The below is the original image, the following is the QGraphicsScene which uses this image in QGraphicsPixmapItem. Can anybody say how to achieve this?.
use this code
QGraphicsScene scene;
QLabel *gif_anim = new QLabel();
QMovie *movie = new QMovie(image);
gif_anim->setMovie(movie);
movie->start();
QGraphicsProxyWidget *proxy = scene.addWidget(gif_anim);
I've been searching the Qt docs for something similar to a GtkSpinner, but found only the possibility to use a QProgressBar with minimum and maximum both set to 0, which however is not what I want.
Is there such a widget in Qt?
QLabel label;
QMovie * movie = new QMovie("animated.gif");
label.setMovie(movie);
movie->start();
As seen in spinner (delay) button thread on QtCentre