hagor
hagor

Reputation: 314

C++ Qt GUI update

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

Upvotes: 1

Views: 1022

Answers (2)

Adrien Leravat
Adrien Leravat

Reputation: 2789

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")

Upvotes: 5

cbuchart
cbuchart

Reputation: 11555

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).

Upvotes: 1

Related Questions