Trouble displaying sequence of images with setPixm

2019-06-09 09:45发布

问题:

I'm trying to display a sequence of images through a Qlabel using setPixmap. I have a QStringList containing the image file names and a for loop which iterates through the images with a 5 second wait after each image. However, only the last image file is ever being displayed. Currently the screen remains blank during the wait of the first iterations until the last image is finally shown. I've read that using a for loop wont work and that I should be using signals and slots instead. I'm new to this concept though and I would really appreciate an example to point me in the right direction.

Here is my current code:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow){
   ui->setupUi(this);
   QStringList images;
   QString imageName;
   images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";

   for(int x=0; x < images.size(); x++){
      imageName = images.at(x);
      this->displayScreen(imageName, 5);
   } 
}

void MainWindow::displayScreen(QString imageName, int wait){
   QTimer t;
   QEventLoop loop;
   QPixmap myImage;

   myImage.load(":/images/" + imageName);
   ui->imageLabel->setPixmap(myImage);
   ui->imageLabel->repaint();

   // 5 second wait between next iteration
   t.start(wait*1000);
   connect(&t, SIGNAL(timeout()), &loop, SLOT(quit()));
   loop.exec();
}

回答1:

The reentrant wait-via-eventloop hack is a great source of hard-to-diagnose bugs. Don't use it. It's very, very rare that you'll need to instantiate your own event loop. Even rather complex projects can entirely avoid it.

You should simply run a timer and react to timer ticks. Here's one example:

#include <QApplication>
#include <QImage>
#include <QGridLayout>
#include <QLabel>
#include <QBasicTimer>

class Widget : public QWidget {
    QGridLayout m_layout;
    QLabel m_name, m_image;
    QStringList m_images;
    QStringList::const_iterator m_imageIt;
    QBasicTimer m_timer;
    void timerEvent(QTimerEvent * ev) {
        if (ev->timerId() == m_timer.timerId()) tick();
    }
    void tick() {
        display(*m_imageIt);
        m_imageIt ++;
        const bool loop = false;
        if (m_imageIt == m_images.end()) {
            if (loop)
                m_imageIt = m_images.begin();
            else
                m_timer.stop();
        }
    }
    void display(const QString & imageName) {
        QImage img(":/images/" + imageName);
        m_name.setText(imageName);
        m_image.setPixmap(QPixmap::fromImage(img));
    }
public:
    Widget(QWidget * parent = 0) : QWidget(parent), m_layout(this) {
        m_images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";
        m_imageIt = m_images.begin();
        m_layout.addWidget(&m_name, 0, 0);
        m_layout.addWidget(&m_image, 1, 0);
        tick();
        m_timer.start(5000, Qt::CoarseTimer, this);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}


回答2:

A code similar to the below must work for the task you mentioned. ( needs cleaning/class organization though )

  QTimer timer;
  int x=0;
  QStringList images;
  QString imageName;


  MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),ui(new Ui::MainWindow){
   ui->setupUi(this);

   images << "redScreen.png" << "blueScreen.png" << "greenScreen.png";

   connect( &timer, SIGNAL(timeout()), this, SLOT(ChangeImageSlot()) );
   timer.start(5000);

}

void ChangeImageSlot()
{
   imageName = images.at(x++);
   this->displayScreen(imageName, 5);

   if( x < images.size() )
      timer.start(5000);
}


回答3:

Best solution is DeepBlack with a couple of QTimer, but if you want at you risk you can try insert an processEvent() function inside the for loop of display image.