Button won't go "invisible" immediately, if right after running CPU intensive tasks

Buttons in my completed project, and another one I just started, works fine, that is when I do myButton.setVisible(false) they immediately go invisible.

Now in a project where I doing some speed tests to better know how to optimize for speed, I have a “Start” button on my screen, and would like to start each “Test stage” individually, so when I click “Start” it will go invisible, then after test loop of stage 1 is done, the button should reappear for me to click and do next stage and so on. Problem is the button does not go invisible. Probably because what comes immediately after myButton.setVisible(false)" is very CPU intensive tasks. Not until task (stage) is done, does the button actually finally go invisible. How do I overcome that. I tried waiting a few seconds with Time::waitForMillisecondCounter (Time::getMillisecondCounter () + 2000);, or even call repaint() several times.

Example;

startButton.setVisible (false);
stage = 1;
speedTest (stage);
repaint(); // Display the test results
// Here I would then turn button back on, so to be ready to do stage 2 at my click, 
but button never turned back "on" so setVisible(true) is not needed, until I solve this problem.

and in speedTest, code part for Stage 1, with runsPerTest variable set to 1,000,000;

combined = 0;

	startTime = Time::getHighResolutionTicks ();
	int intii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (int i = 0; i < 255; i++) { intii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[0] = endTime - startTime;
	combined += intii;

	startTime = Time::getHighResolutionTicks ();
	uint8 uint8ii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (uint8 i = 0; i < 255; i++) { uint8ii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[1] = endTime - startTime;
	combined += uint8ii;

	startTime = Time::getHighResolutionTicks ();
	uint16 uint16ii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (uint16 i = 0; i < 255; i++) { uint16ii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[2] = endTime - startTime;
	combined += uint16ii;

	startTime = Time::getHighResolutionTicks ();
	uint32 uint32ii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (uint32 i = 0; i < 255; i++) { uint32ii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[3] = endTime - startTime;
	combined += uint32ii;

	startTime = Time::getHighResolutionTicks ();
	int32 int32ii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (int32 i = 0; i < 255; i++) { int32ii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[4] = endTime - startTime;
	combined += int32ii;

	startTime = Time::getHighResolutionTicks ();
	int16_t int16_tii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (int16_t i = 0; i < 255; i++) { int16_tii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[5] = endTime - startTime;
	combined += int16_tii;

	startTime = Time::getHighResolutionTicks ();
	int32_t int32_tii = 0;
	for (uint64 loop = 0; loop < runsPerTest; loop++)
	{
		for (int32_t i = 0; i < 255; i++) { int32_tii = i; }
	}
	endTime = Time::getHighResolutionTicks ();
	spentTime[6] = endTime - startTime;
	combined += int32_tii;

You should not do any long lasting operations in the GUI thread. It doesn’t matter if the operations use a lot of CPU or not. You would get the same GUI freeze with a simple Sleep call which isn’t going to use CPU.

You should always try to return to the GUI event loop as soon as possible. If you need to do some long lasting things, you need to run them in other threads. (And you must not directly call any GUI methods from those other threads, which again makes things just a notch harder.)

You do see that the test code, the speedTest function is not inside paint() right? Inside my button listener is where I call speedTest.

The only thing that is in paint() is displaying the finished test results, which is only done after the tests are done.

Painting and responding to things like GUI button clicks all happen in the GUI thread. If you directly start a long lasting loop in response to a button click or similar, you are going to block the GUI from running, so no repaints or other event handling is going to happen. (Until after the time consuming loop or other task has ended.)

Something like this can be used to execute the code in another thread.


// needs the following as a member variable : 

ThreadPool m_threadpool{2}; 

//

m_but.onClick=[this]()
    {
        m_but.setVisible(false);
        m_threadpool.addJob([this]()
        {
            // code is now running in another, non-GUI thread
            double r = wasteSomeCPU(); // call a function that wastes the CPU for a few seconds
            m_result = String(r);
            // no GUI methods like setVisible, repaint etc must be called from other threads,
            // so we have to use the MessageManager static method to run those things back
            // in the GUI thread
            MessageManager::callAsync([this]()
            {
                m_but.setVisible(true);
                repaint();
            });
        });
    };

@DKDiveDude

add this at the top of every function in your project:

    if( MessageManager::getInstance()->isThisTheMessageThread() )
    {
        DBG( "this is the message thread" );
    }

I think you’ll be surprised how much stuff happens on the message thread.

Alright I might try that at one point, thanks. Right now it is not a biggie, because I can just run all stages of my tests, and then only display the results in the end. But your solution may be needed in a more real project.