Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QMessageBox::warning on secondary thread
Qt 6.11 is out! See what's new in the release blog

QMessageBox::warning on secondary thread

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 7 Posters 1.8k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • PerdrixP Perdrix

    @J-Hilk Ok, I've defined a slot in my QMainWindow class:

    void DeepSkyStacker::displayMessageBox(const QString& message, QMessageBox::Icon icon)
    {
    QMessageBox msgBox{ icon, "DeepSkyStacker", message, QMessageBox::Ok , this};
    msgBox.exec();
    }

    The code I want to invoke it from isn't in a Q_OBJECT - in fact it's a free-standing function that's not part of a class. I believe that means I can't emit a signal to display the message.

    I suspect some incantation involving QMetaObject::invokeMethod() is needed?

    If you could help with that, it would be much appreciated.

    Thanks
    David

    J.HilkJ Offline
    J.HilkJ Offline
    J.Hilk
    Moderators
    wrote on last edited by
    #4

    @Perdrix said in QMessageBox::warning on secondary thread:

    If you could help with that, it would be much appreciated.

    depends, how exactly are you invoke that free-standing function in a different thread ?

    can you show that code part ?


    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


    Q: What's that?
    A: It's blue light.
    Q: What does it do?
    A: It turns blue.

    PerdrixP 1 Reply Last reply
    0
    • PerdrixP Perdrix

      @J-Hilk Ok, I've defined a slot in my QMainWindow class:

      void DeepSkyStacker::displayMessageBox(const QString& message, QMessageBox::Icon icon)
      {
      QMessageBox msgBox{ icon, "DeepSkyStacker", message, QMessageBox::Ok , this};
      msgBox.exec();
      }

      The code I want to invoke it from isn't in a Q_OBJECT - in fact it's a free-standing function that's not part of a class. I believe that means I can't emit a signal to display the message.

      I suspect some incantation involving QMetaObject::invokeMethod() is needed?

      If you could help with that, it would be much appreciated.

      Thanks
      David

      J.HilkJ Offline
      J.HilkJ Offline
      J.Hilk
      Moderators
      wrote on last edited by
      #5

      @Perdrix Actually, std::async doesn't require a free function to invoke.

      have you considered creating a temporary parent QObject class to invoke that method ?

      would be the easiest I think


      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      1 Reply Last reply
      0
      • J.HilkJ J.Hilk

        @Perdrix said in QMessageBox::warning on secondary thread:

        If you could help with that, it would be much appreciated.

        depends, how exactly are you invoke that free-standing function in a different thread ?

        can you show that code part ?

        PerdrixP Offline
        PerdrixP Offline
        Perdrix
        wrote on last edited by
        #6

        @J-Hilk

        					const auto readTask = [this, pStackingInfo, firstBitmap = m_vBitmaps.cbegin()](const size_t lightTaskNdx, ProgressBase* pProgress) -> std::pair<std::shared_ptr<CMemoryBitmap>, int>
        					{
        						if (lightTaskNdx >= pStackingInfo->m_pLightTask->m_vBitmaps.size())
        							return { {}, -1 };
        						const int bitmapNdx = FindBitmapIndex(pStackingInfo->m_pLightTask->m_vBitmaps[lightTaskNdx].filePath.c_str());
        						if (bitmapNdx < 0)
        							return { {}, -1 };
        						const auto& lightframeInfo = m_vBitmaps[bitmapNdx];
        						if (lightframeInfo.m_bDisabled)
        							return { {}, -1 };
        
        						ZTRACE_RUNTIME("Stack %s", lightframeInfo.filePath.generic_string().c_str());
        
        						std::shared_ptr<CMemoryBitmap> rpBitmap;
        						if (::LoadFrame(lightframeInfo.filePath, PICTURETYPE_LIGHTFRAME, pProgress, rpBitmap))
        							return { rpBitmap, bitmapNdx };
        						else
        							return { {}, -1 };
        					};
        
        					auto futureForRead = std::async(std::launch::deferred, readTask, 0, m_pProgress); // Load first lightframe synchronously.
        					const auto firstBitmap = m_vBitmaps.cbegin();
        
        					for (size_t i = 0; i < pStackingInfo->m_pLightTask->m_vBitmaps.size() && !bStop; ++i)
        					{
        						auto [pBitmap, bitmapNdx] = futureForRead.get();
        						futureForRead = std::async(std::launch::async, readTask, i + 1, nullptr); // Immediately load next lightframe asynchronously (need to set progress pointer to null).
        
        

        loadFrame() in turn invokes FetchPicture() after doing some stuff.

        In FetchPicture() I wrote this:

        	if (fs::status(filePath).type() != fs::file_type::regular)
        	{
        		QString errorMessage{ QCoreApplication::translate(
        			"DSS::StackingDlg",
        			"%1 does not exist or is not a file").arg(QString::fromStdWString(fileName)) };
        #if defined(_CONSOLE)
        		std::cerr << errorMessage.toUtf8().constData();
        #else
        		QMainWindow* mainWindow{ getMainApplicationWindow() };
        		ZASSERT(nullptr != mainWindow);
        		QMetaObject::invokeMethod(mainWindow,
        			SLOT(displayMessageBox(const QString&, QMessageBox::Icon)),
        			Q_ARG(QString, errorMessage),
        			Q_ARG(QMessageBox::Icon, QMessageBox::Warning));
        		
        #endif
        		return false;
        	}
        

        Unfortunately the displayMessageBox() mf of the DeepSkyStacker class never gets driven?

        jsulmJ J.HilkJ 2 Replies Last reply
        0
        • PerdrixP Perdrix

          @J-Hilk

          					const auto readTask = [this, pStackingInfo, firstBitmap = m_vBitmaps.cbegin()](const size_t lightTaskNdx, ProgressBase* pProgress) -> std::pair<std::shared_ptr<CMemoryBitmap>, int>
          					{
          						if (lightTaskNdx >= pStackingInfo->m_pLightTask->m_vBitmaps.size())
          							return { {}, -1 };
          						const int bitmapNdx = FindBitmapIndex(pStackingInfo->m_pLightTask->m_vBitmaps[lightTaskNdx].filePath.c_str());
          						if (bitmapNdx < 0)
          							return { {}, -1 };
          						const auto& lightframeInfo = m_vBitmaps[bitmapNdx];
          						if (lightframeInfo.m_bDisabled)
          							return { {}, -1 };
          
          						ZTRACE_RUNTIME("Stack %s", lightframeInfo.filePath.generic_string().c_str());
          
          						std::shared_ptr<CMemoryBitmap> rpBitmap;
          						if (::LoadFrame(lightframeInfo.filePath, PICTURETYPE_LIGHTFRAME, pProgress, rpBitmap))
          							return { rpBitmap, bitmapNdx };
          						else
          							return { {}, -1 };
          					};
          
          					auto futureForRead = std::async(std::launch::deferred, readTask, 0, m_pProgress); // Load first lightframe synchronously.
          					const auto firstBitmap = m_vBitmaps.cbegin();
          
          					for (size_t i = 0; i < pStackingInfo->m_pLightTask->m_vBitmaps.size() && !bStop; ++i)
          					{
          						auto [pBitmap, bitmapNdx] = futureForRead.get();
          						futureForRead = std::async(std::launch::async, readTask, i + 1, nullptr); // Immediately load next lightframe asynchronously (need to set progress pointer to null).
          
          

          loadFrame() in turn invokes FetchPicture() after doing some stuff.

          In FetchPicture() I wrote this:

          	if (fs::status(filePath).type() != fs::file_type::regular)
          	{
          		QString errorMessage{ QCoreApplication::translate(
          			"DSS::StackingDlg",
          			"%1 does not exist or is not a file").arg(QString::fromStdWString(fileName)) };
          #if defined(_CONSOLE)
          		std::cerr << errorMessage.toUtf8().constData();
          #else
          		QMainWindow* mainWindow{ getMainApplicationWindow() };
          		ZASSERT(nullptr != mainWindow);
          		QMetaObject::invokeMethod(mainWindow,
          			SLOT(displayMessageBox(const QString&, QMessageBox::Icon)),
          			Q_ARG(QString, errorMessage),
          			Q_ARG(QMessageBox::Icon, QMessageBox::Warning));
          		
          #endif
          		return false;
          	}
          

          Unfortunately the displayMessageBox() mf of the DeepSkyStacker class never gets driven?

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by jsulm
          #7

          @Perdrix said in QMessageBox::warning on secondary thread:

          displayMessageBox

          QMainWindow does not have such a slot, it is your own, right?
          I think you need to cast QMainWindow* to your main window class pointer.

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          1
          • PerdrixP Perdrix

            @J-Hilk

            					const auto readTask = [this, pStackingInfo, firstBitmap = m_vBitmaps.cbegin()](const size_t lightTaskNdx, ProgressBase* pProgress) -> std::pair<std::shared_ptr<CMemoryBitmap>, int>
            					{
            						if (lightTaskNdx >= pStackingInfo->m_pLightTask->m_vBitmaps.size())
            							return { {}, -1 };
            						const int bitmapNdx = FindBitmapIndex(pStackingInfo->m_pLightTask->m_vBitmaps[lightTaskNdx].filePath.c_str());
            						if (bitmapNdx < 0)
            							return { {}, -1 };
            						const auto& lightframeInfo = m_vBitmaps[bitmapNdx];
            						if (lightframeInfo.m_bDisabled)
            							return { {}, -1 };
            
            						ZTRACE_RUNTIME("Stack %s", lightframeInfo.filePath.generic_string().c_str());
            
            						std::shared_ptr<CMemoryBitmap> rpBitmap;
            						if (::LoadFrame(lightframeInfo.filePath, PICTURETYPE_LIGHTFRAME, pProgress, rpBitmap))
            							return { rpBitmap, bitmapNdx };
            						else
            							return { {}, -1 };
            					};
            
            					auto futureForRead = std::async(std::launch::deferred, readTask, 0, m_pProgress); // Load first lightframe synchronously.
            					const auto firstBitmap = m_vBitmaps.cbegin();
            
            					for (size_t i = 0; i < pStackingInfo->m_pLightTask->m_vBitmaps.size() && !bStop; ++i)
            					{
            						auto [pBitmap, bitmapNdx] = futureForRead.get();
            						futureForRead = std::async(std::launch::async, readTask, i + 1, nullptr); // Immediately load next lightframe asynchronously (need to set progress pointer to null).
            
            

            loadFrame() in turn invokes FetchPicture() after doing some stuff.

            In FetchPicture() I wrote this:

            	if (fs::status(filePath).type() != fs::file_type::regular)
            	{
            		QString errorMessage{ QCoreApplication::translate(
            			"DSS::StackingDlg",
            			"%1 does not exist or is not a file").arg(QString::fromStdWString(fileName)) };
            #if defined(_CONSOLE)
            		std::cerr << errorMessage.toUtf8().constData();
            #else
            		QMainWindow* mainWindow{ getMainApplicationWindow() };
            		ZASSERT(nullptr != mainWindow);
            		QMetaObject::invokeMethod(mainWindow,
            			SLOT(displayMessageBox(const QString&, QMessageBox::Icon)),
            			Q_ARG(QString, errorMessage),
            			Q_ARG(QMessageBox::Icon, QMessageBox::Warning));
            		
            #endif
            		return false;
            	}
            

            Unfortunately the displayMessageBox() mf of the DeepSkyStacker class never gets driven?

            J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by J.Hilk
            #8

            @Perdrix ah ok, thats a way to do it :D

            since this connect is a cross threads, the arguments need to be copied over. For QString this is trivial and done out of the box for you.

            But I think you need to register enum QMessageBox::Icon with the meta system so it knows what it is and can copy it over

            https://doc.qt.io/qt-6/qmetatype.html#qRegisterMetaType

            Edit:
            the Point of @jsulm is also more than valid! You'll need to cast it to your actual class, that has the slot :D


            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            Christian EhrlicherC PerdrixP 2 Replies Last reply
            0
            • J.HilkJ J.Hilk

              @Perdrix ah ok, thats a way to do it :D

              since this connect is a cross threads, the arguments need to be copied over. For QString this is trivial and done out of the box for you.

              But I think you need to register enum QMessageBox::Icon with the meta system so it knows what it is and can copy it over

              https://doc.qt.io/qt-6/qmetatype.html#qRegisterMetaType

              Edit:
              the Point of @jsulm is also more than valid! You'll need to cast it to your actual class, that has the slot :D

              Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #9

              Another way would be to use a custom QEvent and simply post it to the global event queue. This way you don't need to know who is the receiver. Good for e.g. status messages from anywhere inside your app to show it in a central widget/status bar.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              JonBJ 1 Reply Last reply
              2
              • J.HilkJ J.Hilk

                @Perdrix ah ok, thats a way to do it :D

                since this connect is a cross threads, the arguments need to be copied over. For QString this is trivial and done out of the box for you.

                But I think you need to register enum QMessageBox::Icon with the meta system so it knows what it is and can copy it over

                https://doc.qt.io/qt-6/qmetatype.html#qRegisterMetaType

                Edit:
                the Point of @jsulm is also more than valid! You'll need to cast it to your actual class, that has the slot :D

                PerdrixP Offline
                PerdrixP Offline
                Perdrix
                wrote on last edited by
                #10

                @J-Hilk So to register QMessageBox::Icon

                Is that as simple as adding this to main()

                	//
                	// Register the QMessageBox::Icon enum as a meta type
                	//
                	qRegisterMetaType<QMessageBox::Icon>();
                
                JonBJ J.HilkJ 2 Replies Last reply
                0
                • Christian EhrlicherC Christian Ehrlicher

                  Another way would be to use a custom QEvent and simply post it to the global event queue. This way you don't need to know who is the receiver. Good for e.g. status messages from anywhere inside your app to show it in a central widget/status bar.

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by
                  #11

                  @Christian-Ehrlicher
                  That is a useful technique, compared to having to create/maintain a QObject. Shame it's not documented somewhere as a useful approach.

                  1 Reply Last reply
                  0
                  • PerdrixP Perdrix

                    @J-Hilk So to register QMessageBox::Icon

                    Is that as simple as adding this to main()

                    	//
                    	// Register the QMessageBox::Icon enum as a meta type
                    	//
                    	qRegisterMetaType<QMessageBox::Icon>();
                    
                    JonBJ Offline
                    JonBJ Offline
                    JonB
                    wrote on last edited by JonB
                    #12

                    @Perdrix
                    May I mention that you can avoid registration of types if you provided a MainWindow::displayWarningBox() method (which can call displayMessageBox() itself) which did not need a QMessageBox::Icon sent to it. This would allow the caller more of an "abstraction" layer which does not need to know so many Qt/message box details.

                    1 Reply Last reply
                    1
                    • PerdrixP Perdrix

                      @J-Hilk So to register QMessageBox::Icon

                      Is that as simple as adding this to main()

                      	//
                      	// Register the QMessageBox::Icon enum as a meta type
                      	//
                      	qRegisterMetaType<QMessageBox::Icon>();
                      
                      J.HilkJ Offline
                      J.HilkJ Offline
                      J.Hilk
                      Moderators
                      wrote on last edited by
                      #13

                      @Perdrix AFAIK yes,

                      maybe you also need
                      https://doc.qt.io/qt-6/qmetatype.html#Q_DECLARE_METATYPE

                      I'm never 100% sure with this. The Meta System is always a lot of magic imho :D


                      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                      Q: What's that?
                      A: It's blue light.
                      Q: What does it do?
                      A: It turns blue.

                      PerdrixP 1 Reply Last reply
                      0
                      • J.HilkJ J.Hilk

                        @Perdrix AFAIK yes,

                        maybe you also need
                        https://doc.qt.io/qt-6/qmetatype.html#Q_DECLARE_METATYPE

                        I'm never 100% sure with this. The Meta System is always a lot of magic imho :D

                        PerdrixP Offline
                        PerdrixP Offline
                        Perdrix
                        wrote on last edited by
                        #14

                        Final code apart from the registration on QMessageBox::Icon in main().

                        	if (fs::status(filePath).type() != fs::file_type::regular)
                        	{
                        		QString errorMessage{ QCoreApplication::translate(
                        			"DSS::StackingDlg",
                        			"%1 does not exist or is not a file").arg(QString::fromStdWString(fileName)) };
                        #if defined(_CONSOLE)
                        		std::cerr << errorMessage.toUtf8().constData();
                        #else
                        		QMainWindow* mainWindow{ getMainApplicationWindow() };
                        
                        		DeepSkyStacker* dss = dynamic_cast<DeepSkyStacker*>(mainWindow);
                        		if (nullptr != dss)
                        		{
                        			bool result = QMetaObject::invokeMethod(dss, "displayMessageBox", Qt::QueuedConnection,
                        				Q_ARG(const QString&, errorMessage),
                        				Q_ARG(QMessageBox::Icon, QMessageBox::Warning));
                        		}
                        		else    // This is here for DeepSkyStackerLive which is not yet Qt 
                        		{
                        			AfxMessageBox(errorMessage.toStdWString().c_str(), MB_OK | MB_ICONWARNING);
                        		}
                        		
                        #endif
                        		return false;
                        	}
                        
                        
                        1 Reply Last reply
                        1
                        • PerdrixP Perdrix has marked this topic as solved on
                        • S Offline
                          S Offline
                          SimonSchroeder
                          wrote on last edited by
                          #15

                          Your approach is good (and working). You can still avoid some hassle by using lambdas instead. Here is what we basically do in our software:

                          QMetaObject::invokeMethod(qApp, [&errorMessage](){ QMessageBox{icon, "DeepSkyStacker", errorMessage}.exec(); });
                          

                          Please note that your thread will just continue running before the message box might even be shown. Sometimes you might want to wait for a response by the user. For this we need extra synchronization. I've put several use case into a nice little header-only library: https://github.com/SimonSchroeder/QtThreadHelper

                          You can have a look how you might solve some of the problems.

                          1 Reply Last reply
                          1
                          • B Offline
                            B Offline
                            Borbixxx
                            wrote on last edited by Borbixxx
                            #16
                            This post is deleted!
                            1 Reply Last reply
                            0

                            • Login

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved