Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QPixmap and GUI application - how end my app?



  • Hello,
    I have a problem with ending my GUI Application. Often I get warnings:

    QPixmap::fromImageInPlace: QPixmap cannot be created without a QGuiApplication
    QPixmap: Must construct a QGuiApplication before a QPixmap
    

    My code:

                while(1)
                {
                         QPixmap pixmap = qApp->screens().at(0)->grabWindow(QDesktopWidget().winId());
                         label->setPixmap(pixmap.scaled(label->width(), label->height(), Qt::KeepAspectRatio));
                }
    

    When I close my App, my App is deleted, but grabWindow function is still working, but there is no qApp when pixmap is ready.

    How can I wait until grabWindow() will be ready and next I'll end my app.

    EDIT:
    One solution is to add "Sleep(1000)" in the MainWindow destructor before delete ui. It works, but I think there is better solution


  • Lifetime Qt Champion

    Hi
    Do you have that code in a thread
    as while(1) is pure evil for a GUI app.



  • @mrjj Yes, the while(1) is in the run() function in the other thread


  • Lifetime Qt Champion

    @TomNow99

    Hi
    You have to fix your thread code then so it can exit the loop on some condition.
    then in the main app do

    connect(qApp, &QApplication::aboutToQuit, Yourthread, [=]{
    Yourthread->quit();
    Yourthread->wait();
    });

    Did you override thread::run ?



  • @mrjj My current code is:

    class threadx: public QThread
    {
        Q_OBJECT
    public:
    
        threadx(clickableLabel * l, int i);
        void run() override;
        void setKill();
    private:
        clickableLabel* label;
        int whichOne;
        bool kill = false;
    };
    
    threadx::threadx(clickableLabel * l, int i): label(l), whichOne(i){}
    
    void threadx::run()
    {
        while(1)
        {
            QPixmap pixmap = qApp->screens().at(0)->grabWindow(QDesktopWidget().winId());
            label->setPixmap(pixmap.scaled(label->width(), label->height(), Qt::KeepAspectRatio));
            
            if(kill)
            {
                terminate();
                wait();
            }
        }
    }
    
    void threadx::setKill()
    {
        kill = true;
    }
    

    And In my mainWindow I only create QVector with xthreads:

    threads.append(new threadx(wektor.last(),i));
    

    After 200 msec I start them:

        for(int i=0;i<threadsAmount;i++)
        {
            threads[i]->start();
        }
    

    And in mainWindow destructor I have:

        for(auto x: threads)
        {
            x->setKill();
        }
    
        Sleep(1000);
    
        delete ui;
    

  • Lifetime Qt Champion

    Hi
    How can this even run ?
    Mine says ASSERT failure in QWidget: "Widgets must be created in the GUI thread.",
    since you have a QLabel ? in the thread.

    Also you use wait inside the run and doc says
    https://doc.qt.io/qt-5/qthread.html#wait
    The thread associated with this QThread object has finished execution (i.e. when it returns from run()).

    so not sure thats is nice inside run

    Also you use terminate which is brute force and should not be used ( often)

    Try this

    MainWindow::~MainWindow()
    {
    
        for (auto x : threads) {
            x->requestInterruption();
            x->wait();
            delete x;
        }
    
    
        delete ui;
    }
    
    void threadx::run()
    {
        while ( ! QThread::currentThread()->isInterruptionRequested()) {
    
            QPixmap pixmap = qApp->screens().at(0)->grabWindow(QDesktopWidget().winId());
            label->setPixmap(pixmap.scaled(label->width(), label->height(), Qt::KeepAspectRatio));
    
        }
    }
    
    

    But im really, really surprised if you can use a Qwidget inside a thread. Its an instant crash for me.


  • Lifetime Qt Champion

    Hi,

    Beside the points made by @mrjj, terminate shall only be used as a last resort. It will not allow for any cleanup to happen properly.



  • @mrjj @SGaist I create QLabels in my main thread. The next threads get only pointers to labels and change pixmaps.

    PseudoCode is:

    my main thread

    for(int i=0; i<screensAmount();i++)
    {
        vectorLabels[i] = new QLabel();  // here there are clickableLabels which are QLabels and I can click on them
        vectorthreadsx[i] = new threadx(vectorLabels[i], i);
        grid->addWidget(vectorLabels[i]);
    }
    QTimer::singleShot(200, this, slotStart());
    
    
    slotStart():
    {
         for(int i=0; i<screensAmount();i++)
         {
             vectorXthreads[i] ->start();
         }
    }
    

    And my threadx Class:

    threadx::threadx(clickableLabel * l, int i): label(l), whichOne(i){}
    
    void threadx::run()
    {
        while(1)
        {
            QPixmap pixmap = qApp->screens().at(0)->grabWindow(QDesktopWidget().winId());
            label->setPixmap(pixmap.scaled(label->width(), label->height(), Qt::KeepAspectRatio));
            
            if(kill)
            {
                terminate();
                wait();
            }
        }
    }
    

    And the second question:

    I would like to get screen in Linux. In windows I have to do only:

    QPixmap pixmap = qApp->screens().at(0)->grabWindow(QDesktopWidget().winId());
    

    But in Linux this isn't work. So what can I do?


  • Lifetime Qt Champion

    You are accessing a GUI element from a different thread which as already said shall not be done. QPixmap is a class ride to the graphics environment so it follows the same rule. If you to process some image in a different thread, use QImage and send that back to the original thread.


Log in to reply