Qt C ++在GUI线程外显示图像(Boost线程)

时间:2017-11-23 13:41:21

标签: multithreading qt boost thread-safety qpixmap

我正在使用VS2015开发一个通过Qt实现其接口的C ++库。在库的一侧,3个提升线程不断加载3个文件夹中的图像。我试图在3个不同的 QLabel (或等效的 QWidgets )中显示这些图像,因此线程主体包含此功能, 特别是通过利用 setPixmap 方法。虽然对函数的调用受boost mutex的保护,但我可能因线程同步而得到异常。寻找解决方案,我已经意识到 QPixmap 小部件不是" 线程安全的" (非重入)。我也尝试使用 QGraphicsView ,但它又依赖于 QPixmap ,因此我遇到了同样的问题。 所以我的问题是:是否存在QPixmap的替代方案,以便在线程安全的Qt中显示图像 方式?

2 个答案:

答案 0 :(得分:0)

我建议不要在GUI编程中进行多线程处理。虽然,Qt提供了一般的多线程支持,恕我直言,这些小部件并没有为此做好充分的准备。

因此,为了实现在不同线程中并发运行的图像加载器,我建议使用以下概念:

每个线程图像加载器都提供私有缓冲区。 GUI不时检查(使用QTimer)这些缓冲区并更新其QPixmap。因为可以从resp访问缓冲区。当然,图像加载器线程以及它们必须受到互斥保护的GUI线程。

我的示例代码testLoadImageMT.cc

#include <atomic>
#include <chrono>
#include <mutex>
#include <thread>
#include <QtWidgets>

// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;

// the fluffy-cat image sample
struct Image {
  guint      width;
  guint      height;
  guint      bytes_per_pixel; /* 3:RGB, 4:RGBA */
  guint8     pixel_data[1];
};
extern "C" const Image fluffyCat;

class ImageLoader {
  private:
    const Image &_img;
    std::atomic<bool> _exit;
    std::mutex _lock;
    QImage _qImg;
    std::thread _thread;

  public: // main thread API

    ImageLoader(const Image &img = fluffyCat):
      _img(img),
      _qImg(img.width, img.height, QImage::Format_RGB888),
      _exit(false), _thread(&ImageLoader::loadImage, std::ref(*this))
    { }

    ~ImageLoader()
    {
      _exit = true;
      _thread.join();
    }

    ImageLoader(const ImageLoader&) = delete;

    void applyImage(QLabel &qLblImg)
    {
      std::lock_guard<std::mutex> lock(_lock);
      qLblImg.setPixmap(QPixmap::fromImage(_qImg));
    }

  private: // thread private

    void loadImage()
    {
      for (;;) {
        { std::lock_guard<std::mutex> lock(_lock);
          _qImg.fill(0);
        }
        size_t i = 0;
        for (int y = 0; y < (int)_img.height; ++y) {
          for (int x = 0; x < (int)_img.width; ++x) {
            const quint32 value
              =  _img.pixel_data[i + 2]
              | (_img.pixel_data[i + 1] << 8)
              | (_img.pixel_data[i + 0] << 16)
              | (0xff << 24);
            i += _img.bytes_per_pixel;
            { std::lock_guard<std::mutex> lock(_lock);
              _qImg.setPixel(x, y, value);
            }
            if (_exit) return; // important: make thread co-operative
          }
          std::this_thread::sleep_for(std::chrono::milliseconds(100)); // slow down CPU cooler
        }
      }
    }
};

int main(int argc, char **argv)
{
  // settings:
  enum { N = 3 }; // number of images loaded/displayed
  enum { Interval = 50 }; // update rate for GUI 50 ms -> 20 Hz (round about)
  // build appl.
  qDebug() << "Qt Version: " << QT_VERSION_STR;
  QApplication app(argc, argv);
  // build GUI
  QWidget qMainWin;
  QVBoxLayout qVBox;
  QLabel *pQLblImgs[N];
  for (int i = 0; i < N; ++i) {
    qVBox.addWidget(
      new QLabel(QString::fromUtf8("Image %1").arg(i + 1)));
    qVBox.addWidget(
      pQLblImgs[i] = new QLabel());
  }
  qMainWin.setLayout(&qVBox);
  qMainWin.show();
  // build image loaders
  ImageLoader imgLoader[N];
  // install timer
  QTimer qTimer;
  qTimer.setInterval(Interval); // ms
  QObject::connect(&qTimer, &QTimer::timeout,
    [&imgLoader, &pQLblImgs]() {
      for (int i = 0; i < N; ++i) {
        imgLoader[i].applyImage(*pQLblImgs[i]);
      }
    });
  qTimer.start();
  // exec. application
  return app.exec();
}

抱歉,我使用的是std::thread而不是boost::thread,因为我对后者没有经验,也没有工作安装。我相信(希望)差异将是微不足道的。 QThread本来是“Qt本地”的选择,但是再次 - 没有经验。

为了简单起见,我只是从链接的二进制图像中复制数据(而不是从文件或其他任何地方加载一个)。因此,必须编译和链接第二个文件,使其成为MCVE - fluffyCat.cc

/* GIMP RGB C-Source image dump (fluffyCat.cc) */

// manually added types (normally provided by glib)
typedef unsigned guint;
typedef unsigned char guint8;

extern "C" const struct {
  guint      width;
  guint      height;
  guint      bytes_per_pixel; /* 3:RGB, 4:RGBA */ 
  guint8     pixel_data[16 * 16 * 3 + 1];
} fluffyCat = {
  16, 16, 3,
  "x\211s\215\232\200gw`fx`at[cx^cw^fu\\itZerWn|ap~cv\204jnzedq^fr^kzfhv^Ra"
  "GRbMWdR\\jXer^qw_\311\256\226\271\253\235\275\264\252\315\277\260\304\255"
  "\231u~i\213\225\207l{fly`jx\\^nRlz_z\206nlx`t~i\221\211s\372\276\243\375"
  "\336\275\376\352\340\356\312\301\235\216\212judgwcl~f\212\226u}\206h\212"
  "\224q\231\237z\232\236{\216\225v\225\230\200\306\274\244\376\360\327\376"
  "\361\331\376\360\341\326\275\272\253\240\244{\203p\202\220xp~e{\204^\222"
  "\230n\212\217g\240\242{\234\236z\214\222r\270\271\247\360\353\340\376\370"
  "\336\376\363\334\375\357\336\310\254\262\232\223\234\\gRfrX\204\220z\212"
  "\225g\225\232j\254\255\177\252\250{\225\226u\304\302\265\374\365\351\376"
  "\375\366\376\367\341\376\361\320\374\346\324\306\241\242\237\232\235n{fj"
  "xckyfu~fUX@VZCfnT\231\231\207\374\374\371\377\372\354\376\376\374\376\376"
  "\372\376\362\332\375\340\301\341\300\264\260\253\262jvdbq\\XkVJTDNTCCG8O"
  "TE\322\321\313\377\377\375\376\376\373\376\377\376\376\376\375\376\374\362"
  "\376\360\342\344\311\306\250\244\254R_PL^HXkT<@2OP@`dP\217\220\177\374\374"
  "\370\377\377\374\376\375\371\377\377\376\376\374\360\377\367\336\376\350"
  "\316\342\303\274\246\236\245jtbXdQTdNQYGU\\KchV\317\315\302\377\376\372\377"
  "\376\367\376\373\360\377\376\367\376\366\337\376\355\312\374\331\271\323"
  "\263\251\216\214\214\\hTP^HL\\FR[LMXI^dW\355\352\342\376\375\366\377\374"
  "\360\376\374\361\376\374\361\376\356\321\374\331\264\374\330\266\330\270"
  "\260\200||Y`SLVE>K9BJ<CN?VYP\347\330\322\376\366\345\376\363\330\376\367"
  "\337\377\372\350\374\342\314\326\243\210\375\350\314\352\317\304shc^`TV`"
  "RVbT>B4IS?PTD\244\232\216\374\355\320\376\354\311\376\351\306\376\362\332"
  "\374\344\321\267\206u\375\362\337\326\274\272\\POMNBT]LNZH:<*<A*TV>OI;\242"
  "\222\207\340\304\243\375\335\262\372\336\272\376\361\334\320\241\212\374"
  "\352\322\266\233\237c\\WFH;MR>\\`F~xP\220\214[pqE\211\202\\g]=\230\214`\313"
  "\266\207\344\303\240\362\336\274\323\257\201\333\304\240\305\252\204\254"
  "\232p\216\206\\\206\203U\232\224b\234\244b\246\257m\220\232`\224\227h~\202"
  "W\206\213]\204\210W\227\227i|\177RvzNlsGrtJwtLz}N{\204RlxF",
};

我在VS2013中进行了编译和测试,在Windows 10(64位)上使用Qt 5.9.2。这是它的外观:

Snapshot of testLoadImageMT

答案 1 :(得分:0)

我使用signal / slot解决了:“非GUI”线程发出信号而不是显示图像,被调用的插槽在GUI线程中绘制QLabel!