WinForm Disappears in Multithreaded C++/CLI Application - multithreading

I'm currently working on an application that needs to utilize a form to block the user from using the main form for a few seconds. Although running the code on the main thread seems to be okay, the labels on the pop up form don't render for about a second when the form first appears. I thought that if I ran that form on a separate thread, the rendering would be a lot smoother. The rendering is now so smooth that the form disappears immediately after rendering. The timer is set for five seconds, with a label on the screen counting down. Here's the relevant code calling the new thread and form:
System::Void MainScreen::runGame(int playerTurn) {
Thread ^ t = gcnew Thread(gcnew ThreadStart(gcnew MainScreen(),
&MainScreen::showModalDialog));
t->Start();
t->Join();
InitializeDice();
startTimer();
}
System::Void MainScreen::showModalDialog() {
GetReady ^ gr = gcnew GetReady();
gr->showModalPopup();
}
And here's the code inside the form:
public:
GetReady(void)
{
InitializeComponent();
}
System::Void showModalPopup() {
this->Show();
startTimer();
}
private: System::Void timerPrep_Tick(System::Object^ sender, System::EventArgs^ e) {
ts = ts->Subtract(TimeSpan(0, 0, 1));
if (ts->TotalSeconds <= 0) {
finishTimer();
} else {
lblTimer->Text = ts->ToString("mm\\:ss");
}
}
System::Void startTimer() {
array<String ^>^ minSec = gcnew array<String ^>(2);
minSec = lblTimer->Text->Split(':');
ts = gcnew TimeSpan(0, Convert::ToInt32(minSec[0]), Convert::ToInt32(minSec[1]));
Thread::Sleep(900);
timerPrep->Start();
}
System::Void finishTimer() {
timerPrep->Stop();
lblTimer->Text = "GO!!!";
Thread::Sleep(900);
this->Close();
}
My ideal solution would be to use a thread to generate the new form, so that rendering in both the main form and the pop up form is smooth.
Things I've tried:
Moving this->Show() every where I can think to put it.
I've added t->Join() to see if the main thread was trying to bring the main window back into focus. Thread t still executes and the timer still runs appropriately with the Join, blocking user input for five seconds - but there is nothing to block the screen.
I've read a few questions on SO, I think the most relevant one is WinForms multithreading issue - although I feel like that might be overkill for this situation.
If you have any ideas on what I need to do to have smooth rendering for both forms, please let me know. Thanks!

Displaying UI on a worker thread is filled with traps and surprises. The chief problem you are having here is that the thread doesn't pump a message loop. Required first of all to ensure that the window can receive Windows messages and to ensure that the thread doesn't immediately terminate. Beyond Jairo's recommendation to use ShowDialog(), the boilerplate solution is:
System::Void MainScreen::showModalDialog() {
Application::Run(gcnew GetReady);
}
There's more, a thread that displays windows should always be an STA thread. An implementation detail for COM and required to get stuff like the clipboard, drag+drop and shell dialogs operating correctly:
Thread ^ t = gcnew Thread(gcnew ThreadStart(this, &MainScreen::showModalDialog));
t->SetApartmentState(ApartmentState::STA);
t->Start();
And you generally have a Z-order problem with windows that are displayed on the main thread. With a nasty habit of your window disappearing behind a window owned by the main thread. No good solution for that.

The problem is showModalPopup uses Show method instead ShowDialog. The thread starts, shows the form (it doesnt block execution), starts the timer, ends and joins to the main thread. The thread where the form was created has been finished and it's here where the winforms multithreading issues comes.
Start the timer first and then show the modal window with ShowDialog. Something like this:
System::Void showModalPopup()
{
startTimer();
this->ShowDialog();
}

Related

Use threads in a ref class form c++ to make parallel works

I am currently working in an HMI. I have to send a message and wait for a response. But if i use a while loop to wait for the response, my HMI stops the interactions waiting for the while loop's end.
I tried to use threads, and I tried to make a threads function in the HMI ref class to execute the function which contains the while loop. But I don't know how to do it. can you help me with that, or give me another way to make parallel work? Thanks
This is the code which contains the while loop from another class
bool Robot::waitArrival(char* at)
{
char _status[256];
strcpy(_status, "Going to ");
strcat(_status, static_cast<char*>(at));
do
{
this->handUpdates->lock();
strcpy(this->status, this->handUpdates->getStatus());
this->handUpdates->unlock();
ArUtil::sleep(1000); //1s
} while (strcmp(this->status, _status) == 0);
strcpy(_status, "Arrived at ");
strcat(_status, static_cast<char*>(at));
if (strcmp(this->status, _status) == 0)
return true;
else
return false;
}
and this is my HMI. By clicking button3 I call the ctr_main() function in the Controller ref class, the ctr_main() function calls the waitArrival() function,
private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) {
Thread t1(&Controleur::ctr_main);
}

My thread closes while the program is running

I'm programming a gmae in c++.net, it's like the space impact. I start the game form and on its load event I create a thread to play a music in the background of the application.
The problem is that when I press the space bar to shoot it also plays a sound, and it stops the main music.
I've already had this issue, but I was told that threads should fix the problem.
I've created the thread and my problem stands. Some help would be appreciated.
private: System::Void GameForm_Load(System::Object^ sender, System::EventArgs^ e)
{
Thread^ newThread = gcnew Thread(gcnew ThreadStart(PlayMusic));
newThread->Name = "MusicaImperio";
newThread->IsBackground = true;
if(newThread->IsBackground == true)
newThread->Start();
//Sleep(3000);
}
This is the load event, and this is the Playmusic method:
static void PlayMusic()
{
PlaySound(TEXT("../Musicas/Game.wav"), NULL, SND_ASYNC | SND_LOOP);
}
if you try to not run it on the background.
Does it work then or still nothing ?
maybe you can see what happens

wxwidgets : How can I kill a progressbar when the worker thread is done?

The user interface has a button Start that when clicked launches a worker thread and the thread does:
- starts local bluetooth radio.
- start searching for remote radios.
while worker thread is doing that I would like to display a progress bar. Here is my plan:
void MyFrame::OnStart(wxCommandEvent& event)
{
/* launch worker thread */
int count = 0;
bool bSkip = false;
while (1)
{
wxProgressBar *p_pb = new wxProgressBar(...);
count++;
Sleep(500);
/* update progress bar to new value */
p_pb->update(count, wxStringEmpty, &bSkip);
/* need a way to get out of here when the worker thread is done */
}
}
/* custom event, fired when bluetooth is done */
void MyFrame::OnBluetoothSearchDone(wxCommandEvent& event)
{
/* we know the thread is done because this event is fired */
/* get data (if any) from the bluetooth module */
}
as you can see after I fire the worker thread the main UI thread is stuck in the progressbar loop and will never catch the event that is sent by the worker thread.
if there is a way to check for pending events then I can do that in the progressbar loop and if there is then I can breakout? OR maybe call the update method on the progressbar from an idle function??
There is no such such thing as wxProgressBar in wxWidgets. If you're using wxProgressDialog and its Update() method, events should be already dispatched because it calls wxYield() internally. If you don't, you can either do it yourself -- but make sure you understand the dangers of doing this! -- or, better, avoid entering a loop at all and just disable the elements of the UI that can't be used and use a timer to update the progress indicator.

How to increase QT Gui thread priority in linux [duplicate]

This question already has an answer here:
Set priority to GUI thread in Qt
(1 answer)
Closed 6 years ago.
Is there a way to set gui thread priority higher than others threads of my app in Linux?
I also know that setPriority function in QThread class, not working in linux.
however, is there a solution to do it?
(i am working with qt4.8)
thanks a lot
You shouldn't need to do that. Your GUI should only be displaying the most recent image. Most likely your design forces the GUI to display the outdated images even if they are not relevant anymore.
The typical way this would be achieved in Qt is by having an image viewer class that only displays the most recently set image:
class ImageViewer : public QWidget {
Q_OBJECT
QImage m_img;
bool m_new;
void paintEvent(QPaintEvent *) {
QPainter p(this);
p.drawImage(0, 0, m_img);
m_new = false;
}
public:
ImageViewer(QWidget * parent = 0) : QWidget(parent), m_new(false) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
Q_SLOT void setImage(const QImage & img) {
if (m_new) qDebug() << "Viewer dropped frame!";
m_img = img;
m_new = true;
if (m_img.size() != size()) setFixedSize(m_img.size());
update();
}
};
You can then send signals connected to the setImage slot. Those signals can come from a QObject that has been moved to another thread.
See here for a complete example.
In GUI thread (for example in main()) execute:
QThread::currentThread()->setPriority(QThread::HighPriority);
See Qt docs for more possible priority values.

Invoke Windows Form Thread and Sleeping

I have a managed c++ application that I start a new thread to do some stuff and update some text boxes, it loops and sleeps at the end of every loop. Because of it sleeping I needed to have it in a new thread so the UI doesn't crash. Then I realized I need to invoke the thread that created the UI to access the textboxes, but now I'm back in the main thread so the sleeping crashes it. How should I approach this.
private: System::Void buttonStartCamera_Click(System::Object^ sender, System::EventArgs^ e)
{
ThreadStart^ threadStart = gcnew ThreadStart(this, &UserInterface::SetText);
Thread^ newThread = gcnew Thread(threadStart);
newThread->Start();
}
void SetText()
{
if (this->textBoxCameraOneX->InvokeRequired)
{
MyDel^ del = gcnew MyDel(this, &UserInterface::SetText);
this->Invoke(del);
}
else
{
int count = 0;
srand (time(NULL));
for (count = 0; count < 20; ++count)
{
for each (Camera^ camera in cameraList)
{
textBoxCameraOneX->Text = count.ToString();
}
Sleep(300);
}
}
}
The best option is likely to refactor this so your Sleep doesn't occur within the SetText method. Your background thread could use a separate method that performs the sleep, and then invokes the proper method to set the text (for one text box at a time) in a loop.
In general, you should keep the methods you use with Control::Invoke as short as possible - they should only include the logic required for your UI work, and not the other functionality.
That being said, in this case, it seems like a System::Windows::Forms::Timer would be more appropriate. You could set the interval to 300, and update a text box one at a time in the timer's Tick event.

Resources