1

I am new with Qt so I got stuck with GUI update. I have 2 classes : ControlWidget in main thread and CameraController in the separate thread.

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    CameraController *cameraController = new CameraController;;
    ControlWidget *main_window = new ControlWidget;
    Thread getImageThread; 
    cameraController->moveToThread(&getImageThread);
    getImageThread.start();
    QTimer get_images_timer;
    QObject::connect(&get_images_timer, SIGNAL(timeout()), cameraController, SLOT(onTimerOut()), Qt::QueuedConnection);
    QObject::connect(cameraController, SIGNAL(sendLabel(QImage)), main_window, SLOT(getImage(QImage)), Qt::QueuedConnection);
    QObject::connect(&get_images_timer, SIGNAL(timeout()), main_window, SLOT(update()), Qt::QueuedConnection);
    get_images_timer.start(2000);
    app.exec();
    return 0;
}

So every 2 seconds I want to get images from camera thread and send them to main thread (that actionally happens, so I have QImage at the main_window object). Then I want to put this QImage to the cam1 and cam2 QLabel. And here I am stuck:

First: when I use setPixmap() method the QLabel.width() and QLabel.height() are different then image.width() and image.height() or pixmap.width() and pixmap.height().

Second: I can not visualize the QLabel. If I do this->ImageLayout->addWidget(cam1) nothing really happens. this->update doesn't help either.

Shall I have an extra worker for GUI Update? What am I doing wrong?

Source code for more information :

CameraController.h

class CameraController : public QObject
{
    Q_OBJECT 
private: 
    CoreApi::InstanceHandle g_hApi;
    CoreApi::DeviceCollectionHandle hDeviceCollection;
    CoreApi::DeviceHandle hDevice;
    CoreApi::CameraPortHandle first_cam;
    Common::FrameHandle frame;
    QPixmap pixmap;
    QImage image;
public: 
    CameraController();
    ~CameraController();
    QLabel outLabel;
public slots:
    void onTimerOut();
signals:
    QImage sendLabel(QImage image);
};

CameraController.cpp

CameraController::CameraController()
{
    try
    {
        this->g_hApi = CoreApi::Instance::initialize();
        this->hDeviceCollection = this->g_hApi->deviceCollection();
        this->hDevice = hDeviceCollection->device(0);
        this->first_cam = hDevice->cameraPort(0);
        first_cam->autoConfigure();
        first_cam->liveStart();
    }
    catch (GeneralException& e)
    {
    std::cout << e.what() << std::endl;
    }
}

CameraController::~CameraController()
{
}

void CameraController::onTimerOut()
{
    if (this->first_cam->liveFrameReady())
    {
        this->frame = first_cam->liveFrame();
        this->image =  QImage((uchar*)this->frame->buffer()->data(), this->frame->dataType()->width(), this->frame->dataType()->height(), QImage::Format::Format_RGB888);
        this->image = this->image.scaled(QSize(this->image.width()/10, this->image.height()/10));
        std::cout << "width = "<<this->image.width() << "height = " << this->image.height() << std::endl;
        emit sendLabel(this->image.copy());
    }
}

ControlWidget.h

class ControlWidget :public QDialog
{
    Q_OBJECT
private:
    QGLCanvas *osCanvas;
    QGridLayout *mainLayout;
    QGridLayout *buttonLayout;
    QVBoxLayout *imageLayout, *settingsLayout;
    QHBoxLayout *controlLayout;
    QListWidget *cameraListWidget, *devicesListWidget;
    QLabel *cameraListLabel, *devicesListLabel, *cameraSettingsLabel, *fpsLabel, *shutterLabel;
    QHBoxLayout *fpsLayout, *shutterLayout;
    QLineEdit *fpsEdit, *shutterEdit;
    QPushButton *saveButton, *saveSettingButton, *applySettingsButton, *chooseFolderButton;
    QTimer* m_timer;
public:
    ControlWidget(QWidget *parent = 0);
    ~ControlWidget();
    QLabel *cam1, *cam2;
    QImage *camera_1, *camera_2;
    void createWidgets();
public slots:
    void getImage(QImage new_frame);
    void displayImages();
signals: 
    void images_loaded();
private slots:
    void onTimeout()
    {
        qDebug() << "Worker::onTimeout get called from controlWidget timer and  ?: " << QThread::currentThreadId();
    };
};

ControlWidget.cpp

ControlWidget::ControlWidget(QWidget *parent)
{
    this->createWidgets();
    this->m_timer = new QTimer;
    connect(this->m_timer, SIGNAL(timeout()),this, SLOT(update()));
    m_timer->start(1000);
}

ControlWidget::~ControlWidget()
{
    delete this->mainLayout;
}


void ControlWidget::createWidgets() 
{
    this->imageLayout = new QVBoxLayout;
    this->cam1 = new QLabel;
    this->cam2 = new QLabel;
    this->imageLayout->addWidget(cam1);
    this->imageLayout->addWidget(cam2);
    this->setLayout(this->imageLayout);
    this->show();
}

void ControlWidget::displayImages()
{
    QLabel tmp_label ;

    std::cout << "********************************************************************************" << std::endl;
    std::cout <<"  camera height  = " <<this->camera_1->height() << "   camera width = " << this->camera_1->width() << std::endl;
    std::cout << "********************************************************************************" << std::endl;
    QPixmap tmp_pixmap = QPixmap::fromImage(this->camera_1->copy());
    std::cout << "PIXMAP WIDTH = " << tmp_pixmap.width() << "Pixmap Height = " << tmp_pixmap.height() <<std::endl;
    std::cout << "LABELWIDTH = "<< tmp_label.width() << "LabelHeight =  "<< tmp_label.height() << std::endl;
    tmp_label.setGeometry(200, 200, tmp_pixmap.width(), tmp_pixmap.height());
    tmp_label.show();
    this->cam1 = &tmp_label;
    this->cam2 = &tmp_label;
    std::cout << "CAM1 Width = " <<this->cam1->width() << std::endl;
    this->imageLayout->addWidget(this->cam1);
    this->imageLayout->addWidget(this->cam2);
}



void ControlWidget::getImage(QImage img)
{
    std::cout << "********************************************************************************" << std::endl;
    std::cout << "  img height  = " << img.height() << "   img width = " << img.width() << std::endl;
    std::cout << "********************************************************************************" << std::endl;
    this->camera_1 = &QImage(img);
    this->camera_2 = &QImage(img);
    this->displayImages();
}
cbuchart
  • 10,847
  • 9
  • 53
  • 93
hagor
  • 304
  • 1
  • 15
  • 2
    Please remove code unnecessary to show the issue. There's lots of unnecessary stuff above. – Kuba hasn't forgotten Monica Mar 21 '17 at 20:05
  • 1
    "Camera_1=&Qimage(Img)" this is very wrong ! You are taking the address of a temporary variable. You open the door to undefined behavior ! – Venom Mar 21 '17 at 21:33
  • Basslo, thank you. I'm really beginner with pointers and memory (I came from matlab and python, so it's a little bit tricky for me yet ) – hagor Mar 21 '17 at 23:44
  • 1
    For ideas how to asynchronously pass images from a producer (e.g. a camera or a disk loader) to a consumer (e.g. a widget), see: [answer 1](http://stackoverflow.com/a/40002585/1329652), [answer 2](http://stackoverflow.com/a/40115212/1329652), [answer 3](http://stackoverflow.com/a/21253353/1329652), [answer 4](http://stackoverflow.com/a/24858047/1329652). – Kuba hasn't forgotten Monica Mar 22 '17 at 15:20
  • [quote] For ideas how to asynchronously pass images from a producer (e.g. a camera or a disk loader) to a consumer (e.g. a widget), see: answer 1, answer 2, answer 3, answer 4. [/quote] Thank you. I will look through them. This is what I have looked for! – hagor Mar 23 '17 at 12:20

2 Answers2

5

Okay, so you have a few design issues here:

  1. tmp_label is created on the stack, and will be destroyed anyway at the end of your displayImages method

  2. Each time a new camera frame is received, your are trying to add your QLabels back to your UI with this->imageLayout->addWidget(this->cam1);. Add them once when constructing the widget instead, and afterward use cam1->setPixmap(...) only.

  3. Maybe I missed it, but I don't see where you set your image in the QLabel. This is typically done with QLabel::setPixmap

And then:

  • Calling update() should not be necessary when relying on standard widgets like you do, a QLabel updates automatically when you set its pixmap
  • You don't actually need to use this-> in C++
  • I don't know what's under your Thread class, but when using QThreads you don't need to pass Qt::QueuedConnection argument for a connect, this is done automatically
  • To exclude a problem related to how to instantiate your UI or how you use layouts, experiment first with a static Qt Designer .ui based interface
  • You can actually easily test the validity of the QPixmap and QImage you read by using QPixmap::save("image.jpg") or QImage::save("image.jpg")
Adrien Leravat
  • 2,731
  • 18
  • 32
  • Thank you. Just fixed and cam1->setPixmap(...) saved my day. I wonder if I can send void* between threads? if I have a void pointer to some memory sent from one thread to another, shall I care about memory at this pointer? Also, can I define label ins the constructor, or it will be a bad way in this case? – hagor Mar 21 '17 at 23:39
  • I changed createWidgets method void ControlWidget::createWidgets() { this->cam1->setGeometry(300, 300, 500, 500); this->cam2->setGeometry(300, 300, 500, 500); this->cam1->setPixmap(QPixmap()); this->imageLayout->addWidget(cam1); this->imageLayout->addWidget(cam2); this->setLayout(this->imageLayout); this->show(); } but it gives me QWindowsGeometry: Unable set geometry message in the console on this->show(). Is there any way to fix this warning? I don't really understand why it happens because I preset geometry. Need I also need to preset a pixmap? – hagor Mar 22 '17 at 00:00
  • When using layouts, the layout itself will set your widgets geometry, so you can remove your "setGeometry" actually. Calling "show()" is also typically done in the parent when needed, like in your main, instead of the constructor. – Adrien Leravat Mar 22 '17 at 09:11
1

In addition to @basslo comment, do not do this->cam1 = &tmp_label;. When tmp_label is destroyed (it is a local variable) it will be removed from the layout it belongs too, so it will never be actually displayed.

Use this->cam1->setPixmap(...) instead to assign the new image and define the size policy to expanding on construction to expanding (this answer offers more information about it).

Community
  • 1
  • 1
cbuchart
  • 10,847
  • 9
  • 53
  • 93