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
Forum Updated to NodeBB v4.3 + New Features

QMessageBox::warning on secondary thread

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 7 Posters 1.6k 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

    					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